Compare commits

..

309 Commits

Author SHA1 Message Date
ringabout
3eddba8b5e stdlib final cleanup 2022-12-01 22:54:04 +08:00
ringabout
911e302d37 'lock levels' are deprecated, now a noop (#20539)
* 'lock levels' are deprecated, now a noop

* fixes tests
2022-10-11 15:17:09 +08:00
Yuriy Glukhov
6987408dae Compile sqlite3 with -O3 flag (#16697) 2021-01-12 14:26:52 +02:00
Juan Carlos
eebe3b59af Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
pyloor
8f6fee9098 adding sqlite3 backup functions (#13346)
* adding sqlite3 backup functions

* changing sleep to sqlite3_sleep to prevent clashes
2020-02-07 07:11:05 +01:00
Federico Ceratto
2efcbd55c3 Fix spellings (#12277) [backport] 2019-09-27 06:02:54 +01:00
Andreas Rumpf
dd89ef9605 prepare sqlite3 wrapper for static linking 2019-09-03 18:43:23 +02:00
Andreas Rumpf
4f602e475c makes the -d:nimIncremental compiler mode compile again 2019-07-21 16:41:46 +02:00
Araq
56cf60b446 minor style changes 2019-07-11 00:28:20 +02:00
Araq
6dbeb3cf46 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Jacek Sieka
cc923b3422 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Andreas Rumpf
553d7c72b4 improvements to memtracking 2017-01-26 11:25:55 +01:00
Araq
8cbb0a3452 Merge branch 'devel' into sighashes 2016-11-28 20:59:30 +01:00
Araq
d67317ce5c Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Araq
ba218f8a80 fixes DLL hell on Windows 2015-09-05 12:03:53 +02:00
Andreas Rumpf
40aa0294f3 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
a4a8cd86ce Merge pull request #3029 from rbehrends/fix-sqlite3-osx
Fix SQLite3 dynlib on OS X.
2015-07-01 00:20:37 +02:00
pdw
4a883c5c5f src/wrappers/s-z - Dropped 'T' from types 2015-05-24 22:50:19 -05:00
Araq
21df5beb3d resolved conflict 2014-09-13 17:59:37 +02:00
Araq
347b0fa0ba updated db_sqlite.nim 2014-08-29 00:16:12 +02:00
Araq
42d017a95e Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Araq
7dd1d096da Merge branch 'devel' of https://github.com/Araq/Nimrod into devel 2014-04-25 19:55:06 +02:00
Araq
e905df2135 Merge branch 'devel' of https://github.com/Araq/Nimrod into devel 2014-04-20 01:19:10 +02:00
Araq
d606c6bf81 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
5c0a036152 fixes #213 2012-10-04 09:49:04 +02:00
Araq
8511346cfb bugfix: optimization of complex constant string concatenations 2012-02-17 02:16:33 +01:00
Araq
edc8f6497c added some missing wrappers 2010-08-24 00:22:36 +02:00
Andreas Rumpf
0bab6fe0b6 examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
9d95883024 fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
38fd3a28f2 SQLite wrapper 2010-02-24 01:04:39 +01:00
rumpf_a@web.de
0d9c28205b continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
2d4cd386c0 added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
54bbc9837f version0.7.10 2009-06-08 08:06:25 +02:00
ringabout
b3c7ddaea0 remove useless defines (#20867) 2022-11-17 23:04:43 +08:00
ringabout
e846f40758 fixes postgres with nimPreviewSlimSystem (#20455) 2022-09-29 23:54:45 +08:00
Artem Klevtsov
2408bf4a33 postgres module fixes (followup #17030) (#17053)
* Fix pqSetSingleRowMode case. Add links to the docs

* Add missing PGContextVisibility enum

* Remove unused PGContextVisibility enum
2021-02-17 01:15:58 +06:00
Artem Klevtsov
b13bfa05a2 Add pqsetsinglerowmode to postgres module (#17030)
* Add pqsetsinglerowmode to postgres module

Very useful to reduce memory usage when fetch a large amount of data.
Documentation: https://www.postgresql.org/docs/current/libpq-single-row-mode.html

* Add missing enum to the ExecStatusType

* Revert entry

* Update ConnStatusType enum
2021-02-14 20:27:44 +06:00
Juan Carlos
df42b85c5c Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Bung
dd343a9a6d add pqserverVersion,pqconnectionNeedsPassword,pqconnectionUsedPassword (#13060) 2020-01-07 17:52:02 +08:00
Araq
a7719009f3 minor style changes 2019-07-11 00:28:20 +02:00
Hitesh Jasani
c6055c0d86 Expose api to disable autoinitializing OpenSSL. (#11247) 2019-05-14 11:08:53 -04:00
Araq
c06bfeb37f removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Jacek Sieka
5e1faeae31 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Araq
5358bf12c0 preparations for more Nimble packages; clear licensing; fixes #2949 2015-06-20 23:24:21 +02:00
pdw
54199d93d5 src/wrappers/c-r - Dropped 'T' from types 2015-05-24 22:43:13 -05:00
Araq
ae74f4cd2c Merge branch 'devel' of https://github.com/Araq/Nimrod into bigbreak
Conflicts:
	lib/impure/db_postgres.nim
	lib/pure/os.nim
	src/wrappers/postgres.nim
2014-10-04 19:40:23 +02:00
Araq
5834a5dac5 cleaned up various modules 2014-09-19 00:54:01 +02:00
Araq
dba3546bbc resolved conflict 2014-09-13 17:59:37 +02:00
Araq
2cece88e0d Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Andreas Rumpf
f76e59a8bf bugfix: return type of gtk2.check_menu_item_new* 2010-03-17 18:58:34 +01:00
Andreas Rumpf
0ab1f41412 examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
28ca6e11ca fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
rumpf_a@web.de
e0ceaba773 continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
ad119a2188 more enhancements for the lib 2010-02-08 22:07:45 +01:00
Andreas Rumpf
fcffc84765 bug concerning constant evaluation fixed 2009-11-26 00:32:07 +01:00
Andreas Rumpf
8fd8905382 added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
99653a0b8b version0.7.10 2009-06-08 08:06:25 +02:00
metagn
e5aa3924fb move widestrs out of system (#20462)
* move widestrs out of system

* fix osproc
2022-10-01 23:35:09 +03:00
cooldome
d94c9dccd7 fix sqlgetdata regression in odbc (#15161)
* fix sqlgetdata import

* fix db_odbc

* more fixes

* fix style
2020-08-07 21:37:54 +01:00
cooldome
76f299e2c0 fix odbc regressions (#15009) [backport]
* fix odbc regressions

* make only necessary changes
2020-07-18 09:21:09 +01:00
Bung
8af6e92606 fix #9771 (#14357)
* fix #9771
* map SQLLEN SQLULEN
* fix proc params take TSqlLen

Co-authored-by: bung87 <crc32@qq.com>
2020-05-16 03:00:06 +08:00
Juan Carlos
96920dace2 Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Araq
8070ba46e0 minor style changes 2019-07-11 00:28:20 +02:00
Andreas Rumpf
45b4eb49ca Merge branch 'devel' into araq-quirky-exceptions 2019-02-08 17:18:17 +01:00
Araq
efbdda55d1 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Jacek Sieka
829b9aad05 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Araq
d0fd779b4c Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Andreas Rumpf
245763c29f Merge pull request #3265 from coffeepots/patch-2
Add widestring versions of SQLPrepare and SQLExecDirect
2015-08-28 18:27:26 +02:00
Araq
deba8d4c18 preparations for more Nimble packages; clear licensing; fixes #2949 2015-06-20 23:24:21 +02:00
pdw
b48e21db34 src/wrappers/c-r - Dropped 'T' from types 2015-05-24 22:43:13 -05:00
Araq
76ca4298f1 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
rumpf_a@web.de
79413cf74c added missing library files 2010-03-07 23:17:37 +01:00
Andreas Rumpf
1a0949c9df examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
e6f0b0b016 fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
rumpf_a@web.de
18bd38d4da continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
7f7f90abbb more enhancements for the lib 2010-02-08 22:07:45 +01:00
Andreas Rumpf
c9d5a1081d added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
13429077a0 version0.7.10 2009-06-08 08:06:25 +02:00
flywind
5e768b34ec use lowercase --define switches (#17283) 2021-03-07 19:08:41 +08:00
Juan Carlos
5673f8fdb6 Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Nindaleth
189f8f8dd1 fix several typos in documentation and comments (#12553) 2019-10-30 09:08:45 +01:00
Araq
13d081aa5d minor style changes 2019-07-11 00:28:20 +02:00
Jacek Sieka
feb550f0b4 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Andreas Rumpf
59d4f801ef mysql.nim: fixes how the DLL version loading works 2018-03-23 19:18:49 +01:00
Dennis Felsing
b5cbae1726 Enable mariadb shared libs for mysql as well (#7199) 2018-02-10 16:42:13 +01:00
Andreas Rumpf
2720c2f818 make MySQL client work with MariaDB; prefer newer versions over older 2018-01-18 11:32:43 +01:00
Dominik Picheta
1bc4196603 Merge pull request #4822 from alphashuro/patch-1
Update mysql.nim
2016-09-24 10:46:11 +02:00
Araq
0f9554fbbc Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Andreas Rumpf
956bcc4715 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
pdw
484ef29df1 src/wrappers/c-r - Dropped 'T' from types 2015-05-24 22:43:13 -05:00
Andreas Rumpf
f4312b4c82 Merge pull request #2140 from FedericoCeratto/devel
Fix some typos
2015-02-16 20:32:24 +01:00
Andreas Rumpf
a511b9f918 Merge pull request #2108 from oderwat/patch-1
Fixing dylib name for OSX
2015-02-12 10:02:53 +01:00
Andreas Rumpf
9034bc173f Merge pull request #1926 from ekarlso/mysql-osx
Support MySQL on OSX
2015-01-11 01:07:56 +01:00
Araq
faadf3eb85 Merge branch 'devel' of https://github.com/Araq/Nim into devel
Conflicts:
	compiler/semexprs.nim
2015-01-07 02:44:22 +01:00
Araq
f6cd14bc61 resolved conflict 2014-09-13 17:59:37 +02:00
Araq
f155fd122d Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Andreas Rumpf
50426e3f23 resolved conflict 2014-04-10 01:47:20 +02:00
Araq
b0624c8eff Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
6f9876dc04 preparations for making 'closure' the default calling convention for proc types 2012-07-16 23:00:57 +02:00
Araq
9528b9d853 added some missing wrappers 2010-08-24 00:22:36 +02:00
Andreas Rumpf
b7adea8e18 examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
d4f66891e6 fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
rumpf_a@web.de
20b5cc6985 continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
b4e02ea65c bugfix: inconsequent tuple usage 2009-12-09 00:41:55 +01:00
Andreas Rumpf
33935a547c added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
08fafdc2c1 overload resolution for proc vars 2009-06-24 17:13:22 +02:00
Andreas Rumpf
16fee27851 version0.7.10 2009-06-08 08:06:25 +02:00
jfilby
58d6166fac Fix several memory leaks in the Postgres wrapper. (#20940) 2022-11-27 20:28:38 +02:00
Andrey Makarov
c12b6a894a Markdown code blocks migration part 7 (#20547) 2022-10-12 17:13:43 +03:00
ringabout
e102f4fb86 refactor dbFormat (#19746)
* refactor dbFormat

* add simple tests
2022-09-29 18:16:42 +08:00
Andrey Makarov
fb463f545f Implement Pandoc Markdown concise link extension (#20304)
* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
2022-09-04 21:52:21 +03:00
ee7
95b4524705 make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
Artem Klevtsov
ef92d6ec76 Improve db_postgres iterators (#18144)
* Fix pqSetSingleRowMode case. Add links to the docs

* Add missing PGContextVisibility enum

* Remove unused PGContextVisibility enum

* Improve db_postgres iterators

* Fix instantRows with DbColumns. Cosmetics.

* Reduce copy&paste in db_postgres

* Move pqclear inside loop
2021-06-03 19:41:57 +07:00
Thomas T. Jarløv
9f35bb3968 Escape %00 / \0 in dbQuote (#18015) [backport:1.4]
Fix https://github.com/nim-lang/Nim/issues/17925
2021-05-15 21:26:15 +02:00
Fröhlich A
2b6e1d4836 Fix insert calling wrong function (#17856)
The `insert` method is calling `tryInsertID`, which ignores the `pkName` parameter.
Calling `tryInsert` instead should be correct.
2021-04-29 14:11:05 +02:00
Danil Yarantsev
196a801c3f Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
60e92168f9 use single backtick (#17100) 2021-02-19 00:47:21 -06:00
Juan Carlos
35dadac3fb db_postgres document how to use it with unix socket (#15187) 2020-08-17 03:39:58 -03:00
Bung
6f7c749ea5 add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Chris Heller
988a4ef900 Check pqntuples > 0 in getValue. Fixes #12973 (#12974) 2019-12-29 08:41:18 -08:00
Federico Ceratto
6c6a7e34e9 Fix spellings (#12277) [backport] 2019-09-27 06:02:54 +01:00
Chris Heller
60f707fdad Skip db_postgres.setRow if getRow returns 0 rows (#11859)
Fixes nim-lang/Nim#11821
2019-08-04 10:28:24 -07:00
Huy
c2daf0055e add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
Zed
aa4184b375 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Araq
83b5a2bc57 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Andreas Rumpf
dcfe341955 fixes merge conflict 2018-08-19 15:14:03 +02:00
Andreas Rumpf
5bc1ac4a51 make tests green again 2017-11-07 12:57:32 +01:00
Andreas Rumpf
16976718da Merge branch 'devel' into araq 2017-11-02 10:46:30 +01:00
Andreas Rumpf
196aad2c57 Merge branch 'devel' into araq 2017-09-15 21:12:59 +02:00
Andreas Rumpf
a202556ac7 Merge branch 'devel' into faster-nimsuggest 2017-02-17 10:48:01 +01:00
Araq
209b4a171e Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
0dcdfa0ab5 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Dominik Picheta
7407d00052 Merge pull request #3514 from vegansk/3513_pgsql_prepstat
Fixed issue 3513: wrong setupQuery realization
2015-11-11 16:09:18 +00:00
Dominik Picheta
043d77d2bc Improved postgres docs and added untestable tests. 2015-10-26 21:10:21 +01:00
JamesP
24c0887f54 fix doco by removing extra ` chars, change a word and line up multi-line text 2015-10-13 07:34:32 +10:00
JamesP
8fb1a0fa9e add doco outlining the two SQL parameter substitution mechanisms for the db_postgres module
adjust doco note: indent by one space

shorten doco example lines, by splitting across multiple lines

shorten doco line widths by splitting long lines into multi-lines

fix to prepare() example in doco "Note:" section
2015-09-17 14:15:33 +10:00
JamesP
a69826615a setupQuery() with SqlQuery take parameter substitution with "?"
add instantRows() with SqlPrepared parameter

fix setupQuery() for SqlQuery to produce a unique identiying query name

add rows() iterator with SqlPrepared parameter

add execAffectedRows for SqlPrepared
2015-09-17 13:08:24 +10:00
JamesP
f9f73aed79 tryExec() with SqlQuery now takes "?" substitution parameters
add tryExec() with SqlPrepared parameter

exec() with SqlQuery now expects "?" parameter substitution
2015-09-17 09:46:41 +10:00
JamesP
2179f45be2 add check to dbFormat() to verify parameter substitution has "?" identifier
add check to prepare() that parameter substitution has "$1" identifier
2015-09-17 09:42:40 +10:00
Andreas Rumpf
2157ebc536 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
8023c2c851 Merge pull request #2866 from nanoant/patch/db-deffered-rows
db: InstantRow and instantRows
2015-06-10 11:06:36 +02:00
pdw
7240056537 lib/impure - Dropped 'T' from types 2015-05-24 22:24:07 -05:00
Andreas Rumpf
88791d9a9e Merge pull request #2337 from kemege/db_charset
Add character set options when opening DB connection, default to UTF-8
2015-03-17 01:32:26 +01:00
Andreas Rumpf
01648fead0 Merge pull request #1889 from ramnes/devel
Happy new year!
2015-01-07 14:14:43 +01:00
Araq
c71afbcd53 Merge branch 'devel' into bigbreak
Conflicts:
	src/db_postgres.nim
	lib/pure/json.nim
	lib/pure/math.nim
	lib/system/atomics.nim
2014-11-03 11:42:36 +01:00
Araq
b984992511 docgen should work again 2014-10-05 03:06:19 +02:00
Araq
b612c42e1c Merge branch 'devel' of https://github.com/Araq/Nimrod into bigbreak
Conflicts:
	src/db_postgres.nim
	lib/pure/os.nim
	lib/wrappers/postgres.nim
2014-10-04 19:40:23 +02:00
Araq
1bb90ad3ae cleaned up various modules 2014-09-19 00:54:01 +02:00
Araq
0e5874a93a resolved conflict 2014-09-13 17:59:37 +02:00
Araq
00e4d53100 Nimrod renamed to Nim 2014-08-28 09:59:26 +02:00
Araq
76ae6168d8 Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Andreas Rumpf
4eb3dda5e4 resolved conflict 2014-04-10 01:47:20 +02:00
Zahary Karadjov
f882499ca2 Merge branch 'master' of github.com:Araq/Nimrod into upstream 2013-12-09 00:26:04 +02:00
Araq
657f3bdfd0 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
3e4072c6ea Merge branch 'master' of github.com:Araq/Nimrod 2012-11-25 11:03:27 +01:00
Araq
f1a64fdb1c fixes #250 2012-11-19 00:16:25 +01:00
Araq
4ade87d840 'assert' hides EAssertionFailsure; stdlib makes use of 'tags' 2012-11-18 01:36:20 +01:00
Araq
593b731002 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-20 18:48:49 +02:00
Araq
9a99aeac00 openarray/varargs split; breaks bootstrapping 2012-08-16 08:34:33 +02:00
Araq
d34c093f8d changed integer promotion rules; breaks bootstrapping and lots of code 2012-07-08 21:03:47 +02:00
Araq
32c87721a7 small bugfixes; documentation generator supports smilies for the forum 2012-05-01 11:14:29 +02:00
Araq
512d1d0bb2 added db_*.getRow 2012-04-24 08:44:36 +02:00
Araq
910dd6bcc9 year 2012 for most copyright headers 2012-01-02 23:07:35 +01:00
Andreas Rumpf
c49e9ff84b fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
8ce203abe0 SQLite wrapper 2010-02-24 01:04:39 +01:00
rumpf_a@web.de
9743e567ae continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
1c8496a3ad cleanup of library docs 2010-02-04 00:47:59 +01:00
Andreas Rumpf
af073ad381 bugfixes for the high-level postgreSQL wrapper 2010-01-29 01:50:39 +01:00
Andreas Rumpf
9e21f3aa24 bugfix: fd_set mustn't be prefixed with struct 2010-01-14 00:05:02 +01:00
Andreas Rumpf
1c2b176d62 bugfix: inconsequent tuple usage 2009-12-09 00:41:55 +01:00
Andreas Rumpf
7c01f52e92 bug concerning constant evaluation fixed 2009-11-26 00:32:07 +01:00
rumpf_a@web.de
20cd512457 version 0.8.2 2009-10-21 10:20:15 +02:00
ringabout
a6997fb8eb fixes ptr to cstring warnings[backport] (#20848)
* fix =#13790 ptr char (+friends) should not implicitly convert to cstring

* Apply suggestions from code review

* first round; compiles on windows

* nimPreviewSlimSystem

* conversion is unsafe, cast needed

* fixes more tests

* fixes asyncnet

* another try another error

* last one

* true

* one more

* why bugs didn't show at once

* add `nimPreviewCstringConversion` switch

* typo

* fixes ptr to cstring warnings[backport]

* add fixes

Co-authored-by: xflywind <43030857+xflywind@users.noreply.github.com>
2022-11-16 17:22:51 +08:00
Andrey Makarov
d0cc21e322 Markdown code blocks migration part 7 (#20547) 2022-10-12 17:13:43 +03:00
ringabout
769869c699 refactor dbFormat (#19746)
* refactor dbFormat

* add simple tests
2022-09-29 18:16:42 +08:00
Andrey Makarov
344d861247 Implement Pandoc Markdown concise link extension (#20304)
* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
2022-09-04 21:52:21 +03:00
ee7
a3eb2381f2 make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
Danil Yarantsev
2834588250 Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
05f1ddc34c use single backtick (#17100) 2021-02-19 00:47:21 -06:00
shirleyquirk
234ecc42fa odbc regression from #14357 (#15417)
#14357 changed from sending 'val', an int, to a pointer to int, which is understandable, but not how SQLSetEnvAttr works.
"Depending on the value of Attribute, ValuePtr will be a 32-bit integer value or point to a null-terminated character string"
2020-09-29 16:08:24 +01:00
cooldome
36a191c9e6 fix sqlgetdata regression in odbc (#15161)
* fix sqlgetdata import

* fix db_odbc

* more fixes

* fix style
2020-08-07 21:37:54 +01:00
Bung
246b6e3ae0 add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Bung
53836903d3 fix #9771 (#14357)
* fix #9771
* map SQLLEN SQLULEN
* fix proc params take TSqlLen

Co-authored-by: bung87 <crc32@qq.com>
2020-05-16 03:00:06 +08:00
Andrew Owen
900d65ae91 Fix typo in docs (#12356) [ci skip] 2019-10-04 00:40:15 -06:00
Federico Ceratto
a17c1f85c3 Fix spellings (#12277) [backport] 2019-09-27 06:02:54 +01:00
Huy
31c1148eb0 add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
Zed
2702689fe7 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Andreas Rumpf
c337a17fb0 Merge branch 'devel' into araq-quirky-exceptions 2019-02-08 17:18:17 +01:00
Araq
40e8c2d744 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Araq
541d93bd90 docs: remove most stale links; refs #9109 2018-09-29 11:23:29 +02:00
Andreas Rumpf
16c646c816 Merge branch 'jlp765-db_odbc2' into devel 2016-05-28 20:12:29 +02:00
Araq
aaf7187fe5 resolved merge conflicts 2016-03-29 15:30:44 +02:00
Araq
255c7670c9 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
1cc74251ee Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
ringabout
0ed7f08bed refactor dbFormat (#19746)
* refactor dbFormat

* add simple tests
2022-09-29 18:16:42 +08:00
Andrey Makarov
9b7320d352 Implement Pandoc Markdown concise link extension (#20304)
* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
2022-09-04 21:52:21 +03:00
Andrey Makarov
03a0fda1bd Markdown code blocks part 6 (#20292) 2022-09-01 02:39:02 +03:00
ee7
730580382c make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
ringabout
ea15babc09 fixes #20153; do not escape _ for mysql [backport] (#20164)
* fixes #20153; do not escape `_` for mysql

* add a test

* Update db_mysql.nim

* Update tdb_mysql.nim

Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>
2022-08-06 05:15:58 +08:00
flywind
eb1a2176e7 enable style:usages for stdlib tests [backport: 1.6] (#19715)
* enable style:usages for stdlib tests

* freeAddrInfo

* more tests

* importc

* bufSize

* fix more

* => parseSql and renderSql
2022-04-13 17:53:02 +08:00
Danil Yarantsev
ce00e9284f Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
942694fd46 use single backtick (#17100) 2021-02-19 00:47:21 -06:00
Andreas Rumpf
abee78c3b0 fixes db_mysql broken quoting; refs c16ee37a71 (r44209990) [backport:1.4] (#16035) 2020-11-18 23:06:16 +01:00
Andreas Rumpf
4ea64743cb fixes #15560 (#15587) 2020-10-15 15:49:00 +02:00
Bung
d0bc5875b0 Fix #15219 SQL escape in db_mysql is not enough (#15234) 2020-09-04 17:04:27 +08:00
Bung
51b0bd3121 add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Andreas Rumpf
e5a46907ab use system.move instead of system.shallowCopy if the GC mode requires it 2019-10-03 16:46:09 +02:00
Huy
4f663a104e add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
Zed
ec08afd847 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Araq
021fe523b1 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Andreas Rumpf
26afc0703a fixes merge conflict 2018-08-19 15:14:03 +02:00
Andreas Rumpf
8c93fc2229 fixed merge conflict 2018-06-04 16:16:50 +02:00
Andreas Rumpf
e8b734dde1 fixes #7200 2018-02-10 16:41:38 +01:00
pgkos
4b4613fc57 Fix segfault in db_mysql fastRows (#5605) 2017-03-26 09:42:15 +02:00
Araq
14c0c64c59 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
68eab87dd2 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Dominik Picheta
a9c1a94037 Merge branch 'jlp765-db_mysqlExamples' into devel 2015-09-05 21:15:25 +01:00
Andreas Rumpf
4be74a1d20 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
bed7bc325f Merge pull request #3264 from jlp765/db_mysql
fixes #3220 src/db_mysql.nim getValue()
2015-09-03 14:09:43 +02:00
Andreas Rumpf
ce8659ac36 Merge pull request #2866 from nanoant/patch/db-deffered-rows
db: InstantRow and instantRows
2015-06-10 11:06:36 +02:00
pdw
d02789dd6e lib/impure - Dropped 'T' from types 2015-05-24 22:24:07 -05:00
Andreas Rumpf
e745c73dea Merge pull request #2337 from kemege/db_charset
Add character set options when opening DB connection, default to UTF-8
2015-03-17 01:32:26 +01:00
Andreas Rumpf
0a735a6f12 Merge pull request #2108 from oderwat/patch-1
Fixing dylib name for OSX
2015-02-12 10:02:53 +01:00
Araq
9e3d2d725e changed formating 2014-12-08 19:49:20 +01:00
Araq
ea72ccbaf8 Merge branch 'devel' into bigbreak
Conflicts:
	src/db_postgres.nim
	lib/pure/json.nim
	lib/pure/math.nim
	lib/system/atomics.nim
2014-11-03 11:42:36 +01:00
Araq
ad8ac587e0 resolved conflict 2014-09-13 17:59:37 +02:00
Araq
747245aa82 Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Simon Hafner
a9d421e40f Merge pull request #1358 from def-/db_mysql-sql
Add missing sql proc for db_mysql
2014-07-14 14:03:10 -05:00
Andreas Rumpf
a271a07b64 resolved conflict 2014-04-10 01:47:20 +02:00
Araq
81dcfba125 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
cc4073fa60 Merge branch 'master' of github.com:Araq/Nimrod 2012-11-25 11:03:27 +01:00
Araq
5e801014fa fixes #250 2012-11-19 00:16:25 +01:00
Araq
0c583a38b4 'assert' hides EAssertionFailsure; stdlib makes use of 'tags' 2012-11-18 01:36:20 +01:00
Araq
e922895883 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-20 18:48:49 +02:00
Araq
1b4db44660 openarray/varargs split; breaks bootstrapping 2012-08-16 08:34:33 +02:00
Araq
acadc5f5eb small bugfixes; documentation generator supports smilies for the forum 2012-05-01 11:14:29 +02:00
Araq
bc84a6e7ce added db_*.getRow 2012-04-24 08:44:36 +02:00
Araq
e8442eaf11 year 2012 for most copyright headers 2012-01-02 23:07:35 +01:00
Andreas Rumpf
5bd3fbb2d6 bugfix: return type of gtk2.check_menu_item_new* 2010-03-17 18:58:34 +01:00
Andreas Rumpf
20de1a342e examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
8f0e58819e fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
1b686605d1 SQLite wrapper 2010-02-24 01:04:39 +01:00
rumpf_a@web.de
1a05787855 continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
a04fc1c0f3 httpserver supports a fixed port number 2010-02-06 12:24:54 +01:00
Andreas Rumpf
1518052813 cleanup of library docs 2010-02-04 00:47:59 +01:00
Andreas Rumpf
b8296988d0 db_mysql.nim added 2010-01-29 08:09:23 +01:00
Andrey Makarov
1b5c7a6941 Markdown code blocks migration part 7 (#20547) 2022-10-12 17:13:43 +03:00
ringabout
70a19c8db8 make more standard libraries work with nimPreviewSlimSystem (#20343)
* make more standard libraries work with `nimPreviewSlimSystem`

* typo

* part two

* Delete specutils.nim

* fixes more tests

* more fixes

* fixes tests

* fixes three more tests

* add formatfloat import

* fix

* last
2022-09-28 02:06:23 +08:00
ee7
4010ad7229 make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
Andreas Rumpf
14c6c1c695 Revert "fix db_sqlite.tryInsertID does raise exceptions in 1.6.0 #19743 (#19744)" (#19745)
This reverts commit b10f0e7bca.
2022-04-25 11:59:37 +02:00
flywind
33f668caaf fix db_sqlite.tryInsertID does raise exceptions in 1.6.0 #19743 (#19744) 2022-04-25 02:20:18 +08:00
Araq
c699f4b5ab db_sqlite: added a space 2022-03-18 14:17:54 +01:00
flywind
eaad754508 fix a sqlite bug (#18669) 2021-08-13 00:21:01 +08:00
Andrey Makarov
f3c0728405 docs: make inline markup more compatible with Markdown (#18053)
fixes https://github.com/timotheecour/Nim/issues/739
2021-05-21 07:54:20 +03:00
Andrey Makarov
b2d26fa9ca fix RST parsing when no indent after enum.item (fix #17249) (#17257) 2021-03-12 10:33:21 +03:00
Danil Yarantsev
89fe268007 Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
fbb62a6c32 use single backtick (#17100) 2021-02-19 00:47:21 -06:00
RokkuCode
5b8ae3cbf4 fixes #16080 (#16091) [backport:1.2]
* fixes #16080

db_sqlite: Error: undeclared field: 'untypedLen'

* redacting fix
2020-11-23 15:15:55 +01:00
Regis Caillaud
42a503b069 Handle BLOB column type in SQLite as binary data (#15681)
* Fixed not handling blob correctly in sqlite
* Fixed setLen commented by mistake
* Added binary example as db_sqlite doc
* Added tests for sqlite binary data
2020-11-02 13:02:55 +01:00
Bung
d078a65a4d add bindParams to db_sqlite (#14408)
* add bindParams to db_sqlite
* no need typeinfo
* remove extro spaces
* reduce bindParams to two branches,raise DbError
* Update src/db_sqlite.nim
* change bindParams to macro,accept varargs[untyped] as params
* change bind blob val to openArray[byte]
* remove unused err type
* explicitly using i32 param
* using import std/private/since
* SQLITE_OK to right hand

* bindParam val int using bindParam overload
* copy data by default
* change exec to template
* remove SqlPrepared procs unused varargs
* fix setupquery for prepared,reset first for exec prepared,add bindNull for literal nil

Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-26 13:44:47 +08:00
Bung
69b6a2dc3a add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Bung
2bfe76908d add SqlPrepared api fix #13559 (#14365)
Co-authored-by: bung87 <crc32@qq.com>
2020-05-16 21:50:04 +08:00
itsumura-h
0ac0f2e2c4 fix #7241 (#13779)
finalize() should run in insert()
2020-04-03 16:38:05 +09:00
Juan Carlos
90fe5e03af Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Ray Imber
8a40b19273 Documentation improvements around the db interface (#12362)
Added more details about the limits and reasoning behind the API.
Came about from this discussion on IRC: https://irclogs.nim-lang.org/04-10-2019.html#16:58:04
2019-10-08 02:24:06 -07:00
Andreas Rumpf
f20f3269fc removed unused imports [refactoring] 2019-08-08 16:51:41 +02:00
Andreas Rumpf
d11230ea13 makes the -d:nimIncremental compiler mode compile again 2019-07-21 16:41:46 +02:00
Huy
79654a8191 add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
jiro
e02ee2c195 db_sqlite: Update documentation (#10330) (#11266) 2019-05-20 03:27:12 +09:00
Zed
fb481c0df8 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Andreas Rumpf
ad45dcd223 Merge branch 'devel' into araq-quirky-exceptions 2019-02-08 17:18:17 +01:00
Araq
8c15451400 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Federico Ceratto
ed588265ca Update example (#9395) 2018-10-16 23:17:37 +01:00
Andreas Rumpf
ebdbdb0ca5 fixes merge conflict 2018-08-19 15:14:03 +02:00
Andreas Rumpf
f503ef5fd9 fixed merge conflict 2018-06-04 16:16:50 +02:00
Araq
e1b3dc208a fixes testament compilation 2018-05-14 17:45:44 +02:00
Jacek Sieka
870869d081 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Andreas Rumpf
a9a982c5e9 make tests green again 2017-11-07 12:57:32 +01:00
Andreas Rumpf
d409e7f4ba Merge branch 'devel' into araq 2017-10-02 08:31:38 +02:00
Araq
fdf460d131 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
a6664bf8e3 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Dominik Picheta
e6e5e86f5c Merge pull request #3294 from jlp765/db_sqliteDoco
Db sqlite doco update (similar to db_mysql)
2015-09-06 23:08:06 +01:00
Andreas Rumpf
46eb616a37 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
457ef2855b Merge pull request #2866 from nanoant/patch/db-deffered-rows
db: InstantRow and instantRows
2015-06-10 11:06:36 +02:00
pdw
118e4667b7 lib/impure - Dropped 'T' from types 2015-05-24 22:24:07 -05:00
Andreas Rumpf
5da8e91358 Merge pull request #2583 from BlaXpirit/test-stdlib
Test stdlib
2015-04-21 15:36:37 +02:00
Andreas Rumpf
4998c99d05 Merge pull request #2337 from kemege/db_charset
Add character set options when opening DB connection, default to UTF-8
2015-03-17 01:32:26 +01:00
Araq
0a9ca2633f fixes #1866 2015-01-10 23:52:05 +01:00
Araq
c7703ec24f updated db_sqlite.nim 2014-08-29 00:16:12 +02:00
Araq
01c6a074c1 Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Andreas Rumpf
bb3f67be0e resolved conflict 2014-04-10 01:47:20 +02:00
Araq
c90a02503b Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
6201c0ba56 Merge branch 'master' of github.com:Araq/Nimrod 2012-11-25 11:03:27 +01:00
Araq
832dc48f89 fixes #250 2012-11-19 00:16:25 +01:00
Araq
d05879916d 'assert' hides EAssertionFailsure; stdlib makes use of 'tags' 2012-11-18 01:36:20 +01:00
Araq
79ced725f0 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-20 18:48:49 +02:00
Araq
714341b1e3 bugfix: leak in db_sqlite.GetValue 2012-10-10 01:05:03 +02:00
Araq
bb03ef88b3 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-10 01:03:04 +02:00
Araq
faefcaf47e openarray/varargs split; breaks bootstrapping 2012-08-16 08:34:33 +02:00
Araq
0e7254f2eb changed integer promotion rules; breaks bootstrapping and lots of code 2012-07-08 21:03:47 +02:00
Araq
9f6219d5cf small bugfixes; documentation generator supports smilies for the forum 2012-05-01 11:14:29 +02:00
Araq
f17a5c0c6b some code formating 2012-04-26 01:16:50 +02:00
Araq
1df6a386a7 added db_*.getRow 2012-04-24 08:44:36 +02:00
Araq
701187b6d7 added system.setControlCHook, system.writeStackTrace 2012-02-19 23:37:37 +01:00
Araq
790515ff98 year 2012 for most copyright headers 2012-01-02 23:07:35 +01:00
Araq
19896f6a42 much more efficient rod file generation 2011-10-18 17:21:51 +02:00
Andreas Rumpf
7c1e294e23 bugfix: return type of gtk2.check_menu_item_new* 2010-03-17 18:58:34 +01:00
Andreas Rumpf
b021a7c91b fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
4cba67edd7 SQLite wrapper 2010-02-24 01:04:39 +01:00
2150 changed files with 39951 additions and 107482 deletions

View File

@@ -7,26 +7,25 @@ body:
- type: markdown
attributes:
value: |
Please provide a minimal code example that reproduces the bug if possible.
Reports with a reproducible example or detailed information will likely receive fixes faster.
- type: textarea
id: nim-version
attributes:
label: Nim Version
description: |
Can be obtained from `nim -v` on the command line along with the OS/architecture.
For development versions, including the commit hash may help.
validations:
required: true
- **Please provide a minimal code example that reproduces the Bug!** :bug:
Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
- type: textarea
id: description
attributes:
label: Description
description: |
Describe the problem. Code example can be given here.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
Use DETAILED DESCRIPTIVE information about the problem.
Here, you go into more details about your Bug report. This section can be a few paragraphs long.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
validations:
required: true
- type: textarea
id: nim-version
attributes:
label: Nim Version
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
validations:
required: true
@@ -35,7 +34,7 @@ body:
attributes:
label: Current Output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
render: text
- type: textarea
@@ -43,14 +42,14 @@ body:
attributes:
label: Expected Output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
render: text
- type: textarea
id: known-workarounds
id: possible-solution
attributes:
label: Known Workarounds
description: Provide any known workarounds.
label: Possible Solution
description: Propose a possible solution.
validations:
required: false
@@ -65,9 +64,12 @@ body:
- type: markdown
attributes:
value: |
- Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
- Thanks for your contributions!, your Bug report will receive feedback from the community soon...
- Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
- If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
- If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
- If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
which should give more context on a compiler crash.
- If it's a regression, you can help us by identifying which version introduced the bug,
see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
or at least try known past releases (eg `choosenim 1.2.0`).
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)

View File

@@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

69
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 365
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 30
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- ARC
- bounty
- Codegen
- Crash
- Generics
- High Priority
- Macros
- Next release
- Showstopper
- Static[T]
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity.
If you think it is still a valid PR, please rebase it on the latest devel;
otherwise it will be closed. Thank you for your contributions.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
# closeComment: >
# Your comment here.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 20
# Limit to only `issues` or `pulls`
only: pulls
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed

View File

@@ -1,38 +0,0 @@
# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901
name: issue comments bisects
on:
issue_comment:
types: created
jobs:
bisects:
if: |
github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE'
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
name: ${{ matrix.platform }}-bisects
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v7
- name: Install OpenSSL (Windows)
if: |
runner.os == 'Windows'
run: choco install openssl.light --version=1.1.1.0 # OpenSSL 3.x removed SSL_library_init
shell: 'powershell'
# v2 wont work here, because uses "hardcoded" nim versions, action "dynamically" finds version with bug.
- uses: jiro4989/setup-nim-action@v1
with:
nim-version: 'devel'
- uses: juancarlospaco/nimrun-action@nim
if: |
runner.os == 'Linux' && contains(github.event.comment.body, '-d:linux' ) ||
runner.os == 'Windows' && contains(github.event.comment.body, '-d:windows') ||
runner.os == 'macOS' && contains(github.event.comment.body, '-d:osx' ) ||
runner.os == 'Linux' && !contains(github.event.comment.body, '-d:linux') && !contains(github.event.comment.body, '-d:windows') && !contains(github.event.comment.body, '-d:osx')
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -2,7 +2,8 @@ name: Nim Docs CI
on:
push:
paths:
- 'compiler/**.nim'
- 'compiler/docgen.nim'
- 'compiler/renderverbatim.nim'
- 'config/nimdoc.cfg'
- 'doc/**.rst'
- 'doc/**.md'
@@ -17,7 +18,8 @@ on:
pull_request:
# Run only on changes on these files.
paths:
- 'compiler/**.nim'
- 'compiler/docgen.nim'
- 'compiler/renderverbatim.nim'
- 'config/nimdoc.cfg'
- 'doc/**.rst'
- 'doc/**.md'
@@ -41,11 +43,11 @@ jobs:
target: [linux, windows, osx]
include:
- target: linux
os: ubuntu-22.04
os: ubuntu-20.04
- target: windows
os: windows-latest
os: windows-2019
- target: osx
os: macos-15
os: macos-11
name: ${{ matrix.target }}
runs-on: ${{ matrix.os }}
@@ -53,7 +55,7 @@ jobs:
steps:
- name: 'Checkout'
uses: actions/checkout@v7
uses: actions/checkout@v3
with:
fetch-depth: 2
@@ -109,7 +111,7 @@ jobs:
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: crazy-max/ghaction-github-pages@v5
uses: crazy-max/ghaction-github-pages@v3
with:
build_dir: doc/html
env:

View File

@@ -4,8 +4,6 @@ on:
push:
branches:
- 'devel'
- 'version-2-2'
- 'version-2-0'
- 'version-1-6'
- 'version-1-2'
@@ -18,13 +16,9 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
batch: ["0_3", "1_3", "2_3"] # list of `index_num`
include:
- os: ubuntu-latest
cpu: amd64
- os: macos-latest
cpu: arm64
os: [ubuntu-20.04, macos-11]
cpu: [amd64]
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
runs-on: ${{ matrix.os }}
timeout-minutes: 60 # refs bug #18178
@@ -33,27 +27,26 @@ jobs:
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
steps:
- name: 'Checkout'
uses: actions/checkout@v7
uses: actions/checkout@v2
with:
fetch-depth: 2
- name: 'Install node.js'
uses: actions/setup-node@v6
- name: 'Install node.js 16.x'
uses: actions/setup-node@v2
with:
node-version: 24
node-version: '16.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
run: |
sudo apt-get update -qq
sudo apt-fast update -qq
DEBIAN_FRONTEND='noninteractive' \
sudo apt-get install --no-install-recommends -yq \
sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev liblapack-dev libpcre3 xorg-dev
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3
# XXX can't find boehm and gtk on macos 13
- name: 'Install dependencies (Windows)'
if: runner.os == 'Windows'
shell: bash

View File

@@ -11,20 +11,20 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04]
os: [ubuntu-20.04]
cpu: [amd64]
name: '${{ matrix.os }}'
runs-on: ${{ matrix.os }}
steps:
- name: 'Checkout'
uses: actions/checkout@v7
uses: actions/checkout@v2
with:
fetch-depth: 2
- name: 'Install node.js'
uses: actions/setup-node@v6
- name: 'Install node.js 16.x'
uses: actions/setup-node@v2
with:
node-version: 24
node-version: '16.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
@@ -34,6 +34,17 @@ jobs:
sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
valgrind libc6-dbg libblas-dev xorg-dev
- name: 'Install dependencies (macOS)'
if: runner.os == 'macOS'
run: brew install boehmgc make sfml gtk+3
- name: 'Install dependencies (Windows)'
if: runner.os == 'Windows'
shell: bash
run: |
set -e
. ci/funs.sh
nimInternalInstallDepsWindows
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
- name: 'Add build binaries to PATH'
shell: bash
@@ -60,7 +71,7 @@ jobs:
run: nim c -r -d:release ci/action.nim
- name: 'Comment'
uses: actions/github-script@v9
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');

View File

@@ -1,25 +0,0 @@
# https://github.com/actions/stale#usage
name: Stale pull requests
on:
schedule:
- cron: '0 0 * * *' # Midnight.
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
with:
days-before-pr-stale: 365
days-before-pr-close: 30
days-before-issue-stale: -1
days-before-issue-close: -1
exempt-pr-labels: "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]"
exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority"
stale-pr-message: >
This pull request is stale because it has been open for 1 year with no activity.
Contribute more commits on the pull request and rebase it on the latest devel,
or it will be closed in 30 days. Thank you for your contributions.
close-pr-message: >
This pull request has been marked as stale and closed due to inactivity after 395 days.

5
.gitignore vendored
View File

@@ -67,8 +67,6 @@ testament.db
/csources
/csources_v1
/csources_v2
/csources_v3
/dist/
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
@@ -87,7 +85,6 @@ tweeter_test.db
/tests/megatest.nim
/tests/ic/*_temp.nim
/tests/ic/*_mm/
/tests/navigator/*_temp.nim
@@ -112,5 +109,3 @@ htmldocs
nimdoc.out.css
# except here:
!/nimdoc/testproject/expected/*
pkgs/
/compiler/compiler/

62
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,62 @@
# xxx unused, out of date
image: ubuntu:18.04
stages:
- pre-build
- build
- deploy
- test
.linux_set_path: &linux_set_path_def
before_script:
- export PATH=$(pwd)/bin${PATH:+:$PATH}
tags:
- linux
.windows_set_path: &win_set_path_def
before_script:
- set PATH=%CD%\bin;%PATH%
tags:
- windows
build-windows:
stage: build
script:
- ci\build.bat
artifacts:
paths:
- bin\nim.exe
- bin\nimd.exe
- compiler\nim.exe
- koch.exe
expire_in: 1 week
tags:
- windows
deploy-windows:
stage: deploy
script:
- koch.exe winrelease
artifacts:
paths:
- build/*.exe
- build/*.zip
expire_in: 1 week
tags:
- windows
- fast
test-windows:
stage: test
<<: *win_set_path_def
script:
- call ci\deps.bat
- nim c testament\tester
- testament\tester.exe all
tags:
- windows
- fast

View File

@@ -20,32 +20,32 @@ jobs:
strategy:
matrix:
Linux_amd64:
vmImage: 'ubuntu-24.04'
vmImage: 'ubuntu-20.04'
CPU: amd64
# regularly breaks, refs bug #17325
# Linux_i386:
# # on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
# vmImage: 'ubuntu-18.04'
# CPU: i386
OSX_arm64:
vmImage: 'macos-15'
CPU: arm64
OSX_arm64_cpp:
vmImage: 'macos-15'
CPU: arm64
Linux_i386:
# on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
# g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
vmImage: 'ubuntu-18.04'
CPU: i386
OSX_amd64:
vmImage: 'macOS-11'
CPU: amd64
OSX_amd64_cpp:
vmImage: 'macOS-11'
CPU: amd64
NIM_COMPILE_TO_CPP: true
Windows_amd64_batch0_3:
vmImage: 'windows-2025'
vmImage: 'windows-2019'
CPU: amd64
# see also: `NIM_TEST_PACKAGES`
NIM_TESTAMENT_BATCH: "0_3"
Windows_amd64_batch1_3:
vmImage: 'windows-2025'
vmImage: 'windows-2019'
CPU: amd64
NIM_TESTAMENT_BATCH: "1_3"
Windows_amd64_batch2_3:
vmImage: 'windows-2025'
vmImage: 'windows-2019'
CPU: amd64
NIM_TESTAMENT_BATCH: "2_3"
@@ -73,19 +73,17 @@ jobs:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
displayName: 'Install node.js 20.x'
versionSpec: '16.x'
displayName: 'Install node.js 16.x'
condition: and(succeeded(), eq(variables['skipci'], 'false'))
- bash: |
set -e
. ci/funs.sh
echo_run sudo add-apt-repository universe
echo_run sudo apt-get update -qq
echo_run sudo apt-fast update -qq
DEBIAN_FRONTEND='noninteractive' \
echo_run sudo apt-get install --no-install-recommends -yq \
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
echo_run sudo apt-fast install --no-install-recommends -yq \
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
displayName: 'Install dependencies (amd64 Linux)'
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
@@ -102,16 +100,15 @@ jobs:
Pin-Priority: 1001
EOF
# echo_run sudo apt-get update -qq
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
# echo_run sudo apt-fast update -qq
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
# `could not load: libffi.so` during dynamic loading.
DEBIAN_FRONTEND='noninteractive' \
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
cat << EOF > bin/gcc
#!/bin/bash
@@ -133,7 +130,6 @@ jobs:
- bash: brew install boehmgc make sfml
displayName: 'Install dependencies (OSX)'
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
# XXX can't find boehm on macos 13
- bash: |
set -e

View File

@@ -14,7 +14,7 @@ else
fi
# Find out where the pretty printer Python module is
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py"
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py"
# Run GDB with the additional arguments that load the pretty printers
# Set the environment variable `NIM_GDB` to overwrite the call to a

View File

@@ -3,7 +3,7 @@ for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i)
for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi)
set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py
set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\nim-gdb.py
set @NIM_GDB=gdb.exe
@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt

View File

@@ -24,6 +24,6 @@ if not exist %nim_csources% (
cd ..
copy /y bin\nim.exe %nim_csources%
)
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
koch tools --skipUserCfg --skipParentCfg --hints:off
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
koch tools --skipUserCfg --skipParentCfg --hints:off

View File

@@ -1,159 +1,273 @@
# v2.x.x - yyyy-mm-dd
# v2.0.0 - yyyy-mm-dd
## Changes affecting backward compatibility
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response, it followed Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`.
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
floating point numbers that are minimal in size and possess round-trip and correct
rounding guarantees (via the
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
- `addr` is now available for all addressable locations,
`unsafeAddr` is now deprecated and an alias for `addr`.
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
avoid conflicts with `system.default`, so named argument usage for this
parameter like `getOrDefault(..., default = ...)` will have to be changed.
- Certain definitions from the default `system` module have been moved to
the following new modules:
- Typedesc field access on object/tuple types (e.g. `Foo[int].val`) is now
restricted to `typeof` context. Use `--legacy:typedescFieldAccess` to restore
the previous behavior of allowing it outside `typeof`.
- `std/syncio`
- `std/assertions`
- `std/formatfloat`
- `std/objectdollar`
- `std/widestrs`
- `std/typedthreads`
- `std/sysatomics`
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
In the future, these definitions will be removed from the `system` module,
and their respective modules will have to be imported to use them.
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
may be used.
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
errors.
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
symbols in the `system` module:
- Aliases with `Error` suffix to exception types that have a `Defect` suffix
(see [exceptions](https://nim-lang.org/docs/exceptions.html)):
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
`DeadThreadError`, `NilAccessError`
- `addQuitProc`, replaced by `exitprocs.addExitProc`
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
- `TaintedString`, formerly a distinct alias to `string`
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
in the `system` module.
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore.
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
- The `gc:v2` option is removed.
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- The `mainmodule` and `m` options are removed.
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
- The `threads:on` option is now the default.
- Adds the switch `--mangle:nim|cpp`, which selects `nim` or `cpp` style name mangling when used with `debuginfo` on, defaults to `cpp`.
- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via
`experimental:flexibleOptionalParams`.
- The second parameter of `succ`, `pred`, `inc`, and `dec` in `system` now accepts `SomeInteger` (previously `Ordinal`).
- Automatic dereferencing (experimental feature) is removed.
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
- The `Math.trunc` polyfill for targeting Internet Explorer was
previously included in most JavaScript output files.
Now, it is only included with `-d:nimJsMathTruncPolyfill`.
If you are targeting Internet Explorer, you may choose to enable this option
or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
Nim uses `Math.trunc` for the division and modulo operators for integers.
- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
`sink` for optimization purposes.
- Procedure compatibility also checks the backend representation of the
parameter and result types, not just their source-level shape. Use
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
- The `nimPreviewDotLikeOps` define is going to be removed or deprecated.
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
```nim
type Foo = distinct ptr int
# Before:
var x: Foo = nil
# After:
var x: Foo = Foo(nil)
```
- Removed two type pragma syntaxes deprecated since 0.20, namely
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`.
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
are no longer experimental.
- Removed the `nimIncrSeqV3` define.
- `macros.getImpl` for `const` symbols now returns the full definition node
(as `nnkConstDef`) rather than the AST of the constant value.
- Lock levels are deprecated, now a noop.
- ORC is now the default memory management strategy. Use
`--mm:refc` for a transition period.
- `strictEffects` are no longer experimental.
Use `legacy:laxEffects` to keep backward compatibility.
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`.
- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning.
This warning will become an error in future versions! Use a `cast` operation
like `cast[cstring](x)` instead.
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
- Object fields now support default values, see https://nim-lang.github.io/Nim/manual.html#types-default-values-for-object-fields for details.
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
## Standard library additions and changes
[//]: # "Additions:"
- Added `system.readRawDataStable`, a companion to `readRawData` that returns a
raw `ptr UncheckedArray[char]` into a string's character data which stays valid
across moves and copies of the string value. It is available under every string
implementation (refc, ARC/ORC and `--strings:sso`) with the same signature, so
code can pin an interior buffer pointer today and be ready for `--strings:sso`
without `when declared` guards. Under `--strings:sso` it promotes a small inline
string to its heap representation first; under the other implementations the data
is already heap-resident, so it is equivalent to `readRawData`.
- `setutils.symmetricDifference` along with its operator version
`` setutils.`-+-` `` and in-place version `setutils.toggle` have been added
to more efficiently calculate the symmetric difference of bitsets.
- `strutils.multiReplace` overload for character set replacements in a single pass.
Useful for string sanitation. Follows existing multiReplace semantics.
- `std/files` adds:
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
- New file operation procs with `Path` support:
- `getFilePermissions`, `setFilePermissions` for managing permissions
- `tryRemoveFile` for file deletion
- `copyFile` with configurable buffer size and symlink handling
- `copyFileWithPermissions` to preserve file attributes
- `copyFileToDir` for copying files into directories
- `std/dirs` adds:
- New directory operation procs with `Path` support:
- `copyDir` with special file handling options
- `copyDirWithPermissions` to recursively preserve attributes
- `system.setLenUninit` now supports refc, JS and VM backends.
- `system.setLenUninit` for the `string` type. Allows setting length without initializing new memory on growth.
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
Modes include `Nim` (default, fully compatible) and two new experimental modes:
`Lax` and `Gnu` for different option parsing behaviors.
- `std/symlinks.expandSymlink` now supports Windows symlinks and junctions with
POSIX-like single-hop `readlink` semantics.
- `std/nre2` is added to replace deprecated NRE.
- `system.typeof` adds a new parameter `modifierMode` to specify how type modifiers are handled.
[//]: # "Changes:"
- OpenSSL 3 is now supported.
- `macros.parseExpr` and `macros.parseStmt` now accept an optional
filename argument for more informative errors.
- Module `colors` expanded with missing colors from the CSS color standard.
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
- The `md5` module now works at compile time and in JavaScript.
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables.
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
making limiting it to just the first char (`last = 0`) valid.
- `random.rand` now works with `Ordinal`s.
- Undeprecated `os.isvalidfilename`.
- `std/oids` now uses `int64` to store time internally (before it was int32).
- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`.
- `std/uri.Uri.isIpv6` is now exported.
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
Use https://github.com/nitely/nim-regex or `std/nre2`.
See: https://github.com/nim-lang/Nim/issues/23668.
- `std/pegs` now correctly lexes UTF-8 bytes inside bare identifier-style
terminals, so case-insensitive matching of non-ASCII terms (e.g. ``\i café``)
works without single-quoting.
- `std/uri`: The `?` operator now appends query parameters to an existing query
string instead of replacing it. Fixes [#19782](https://github.com/nim-lang/Nim/issues/19782).
- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`.
- `std/jsfetch.newFetchOptions` now has default values for all parameters
- `std/jsformdata` now accepts `Blob` data type.
[//]: # "Additions:"
- Added ISO 8601 week date utilities in `times`:
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
- Added a `initDateTime` overload to create a datetime from an ISO week date.
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
- Added new modules which were part of `std/os`:
- Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling.
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
- Added `sep` parameter in `std/uri` to specify the query separator.
- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
in `jscore` for JavaScript targets.
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
- Added `complex.sgn` for obtaining the phase of complex numbers.
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
`toggleAttribute`, and `matches` to `std/dom`.
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)
- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460
- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse.
- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse.
- Added `safe` parameter to `base64.encodeMime`.
[//]: # "Deprecations:"
- Deprecated `selfExe` for Nimscript.
- Deprecated `std/sums`.
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
Now only `byte` and `char` are supported.
[//]: # "Removals:"
- Removed deprecated module `parseopt2`.
- Removed deprecated module `sharedstrings`.
- Removed deprecated module `dom_extensions`.
- Removed deprecated module `LockFreeHash`.
- Removed deprecated module `events`.
- Removed deprecated `oids.oidToString`.
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
- Removed deprecated `jsre.test` and `jsre.toString`.
- Removed deprecated `math.c_frexp`.
- Removed deprecated `` httpcore.`==` ``.
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types.
- Removed deprecated `osproc.poDemon`, symbol with typo.
## Language changes
- An experimental option `--experimental:typeBoundOps` has been added that
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
reliable for nominal types across indirect/restricted imports.
- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma
which can be used to disable certain effects in proc types.
- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
for the right-hand side of type definitions in type sections. Previously
they would error with "invalid indentation".
- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
In the command line, this is defined as `-d:a.b.c`. Older versions can
use accents as in ``defined(`a.b.c`)`` to access such defines.
- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
- Templates now accept macro pragmas.
- Macro pragmas for var/let/const sections have been redesigned in a way that works
similarly to routine macro pragmas. The new behavior is documented in the
[experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
allowing multiple type definitions to be injected in place of the original type definition.
```nim
import macros
macro multiply(amount: static int, s: untyped): untyped =
let name = $s[0].basename
result = newNimNode(nnkTypeSection)
for i in 1 .. amount:
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
type
Foo = object
Bar {.multiply: 3.} = object
x, y, z: int
Baz = object
# becomes
type
Foo = object
Bar1 = object
x, y, z: int
Bar2 = object
x, y, z: int
Bar3 = object
x, y, z: int
Baz = object
```
- Redefining templates with the same signature implicitly was previously
allowed to support certain macro code. A `{.redefine.}` pragma has been
added to make this work explicitly, and a warning is generated in the case
where it is implicit. This behavior only applies to templates, redefinition
is generally disallowed for other symbols.
- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
has been implemented for a variety of basic cases. For example, code like the following now compiles:
```nim
# objs.nim
import std/hashes
type
Obj* = object
x*, y*: int
z*: string # to be ignored for equality
proc `==`*(a, b: Obj): bool =
a.x == b.x and a.y == b.y
proc hash*(a: Obj): Hash =
$!(hash(a.x) &! hash(a.y))
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
```
```nim
# main.nim
{.experimental: "typeBoundOps".}
from objs import Obj # objs.hash, objs.`==` not imported
import std/tables
- `cstring` is now accepted as a selector in `case` statements, removing the
need to convert to `string`. On the JS backend, this is translated directly
to a `switch` statement.
var t: Table[Obj, int]
t[Obj(x: 3, y: 4, z: "debug")] = 34
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
```
- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
for more information.
## Compiler changes
- Fixed a bug where `sizeof(T)` inside a `typedesc` template called from a generic type's
`when` clause would error with "'sizeof' requires '.importc' types to be '.completeStruct'".
The issue was that `hasValuelessStatics` in `semtypinst.nim` didn't recognize
`tyTypeDesc(tyGenericParam)` as an unresolved generic parameter.
- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
reality better. (Nim moved away from all techniques based on "tracing".)
- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
without requiring `-d:nimVersion140` which is now a noop.
- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
related functions produced on the backend. This prevents conflicts with other Nim
static libraries.
- When compiling for Release the flag `-fno-math-errno` is used for GCC.
## Tool changes
- Added `--raw` flag when generating JSON docs to not render markup.
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
- Added `--styleCheck:warning` flag to treat style check violations as warnings.
## Documentation changes
- Added documentation for the `completeStruct` pragma in the manual.
- Nim now supports Nimble version 0.14 which added support for lock-files. This is done by
a simple configuration change setting that you can do yourself too. In `$nim/config/nim.cfg`
replace `pkgs` by `pkgs2`.

33
changelogs/changelog.md Normal file
View File

@@ -0,0 +1,33 @@
# v1.xx.x - yyyy-mm-dd
## Changes affecting backward compatibility
## Standard library additions and changes
- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433).
### New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall
This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
```sh
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
$ sh build.sh # per unix install instructions
$ bin/nim c koch # per unix install instructions
$ ./koch boot -d:release # per unix install instructions
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
```
This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular).
## Language changes
## Compiler changes
## Tool changes

View File

@@ -53,7 +53,7 @@
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
now produces a `CountTable` with values corresponding to the number of occurrences
now produces a `CountTable` with values correspoding to the number of occurrences
of the key in the input. It used to produce a table with all values set to `1`.
Counting occurrences in a sequence used to be:
@@ -339,7 +339,7 @@ for i in a..b:
- Fixed "ReraiseError when using try/except within finally block"
([#5871](https://github.com/nim-lang/Nim/issues/5871))
- Fixed "Range type inference leads to counter-intuitive behaviour"
- Fixed "Range type inference leads to counter-intuitive behvaiour"
([#5854](https://github.com/nim-lang/Nim/issues/5854))
- Fixed "JSON % operator can fail in extern procs with dynamic types"
([#6385](https://github.com/nim-lang/Nim/issues/6385))
@@ -403,7 +403,7 @@ for i in a..b:
([#6589](https://github.com/nim-lang/Nim/issues/6589))
- Fixed "Generated c code calls function twice"
([#6292](https://github.com/nim-lang/Nim/issues/6292))
- Fixed "Range type inference leads to counter-intuitive behaviour"
- Fixed "Range type inference leads to counter-intuitive behvaiour"
([#5854](https://github.com/nim-lang/Nim/issues/5854))
- Fixed "New backward indexing is too limited"
([#6631](https://github.com/nim-lang/Nim/issues/6631))

View File

@@ -22,7 +22,7 @@
- We removed `unicode.Rune16` without any deprecation period as the name
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
and we didn't find any usage of it in the wild. If you still need it, add this
and we didn't find any usages of it in the wild. If you still need it, add this
piece of code to your project:
```nim
type

View File

@@ -11,7 +11,7 @@
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
* Fix how `relativePath` handles case sensitivity
* Fix how `relativePath` handle case sensitiviy
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
* VM: no special casing for big endian machines
@@ -43,7 +43,7 @@
* threadpool: fix link in docs (#12258)
* Fix spellings (#12277)
* fix #12278, don't expose internal PCRE documentation
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
* Fix typo in docs
* Fix reference to parseSpec proc in readme
* [doc/tut1] removed discard discussion in comments

View File

@@ -169,7 +169,7 @@ echo f
- The Nim compiler now supports a new pragma called ``.localPassc`` to
pass specific compiler options to the C(++) backend for the C(++) file
that was produced from the current Nim module.
- The compiler now infers "sink parameters". To disable this for a specific routine,
- The compiler now inferes "sink parameters". To disable this for a specific routine,
annotate it with `.nosinks`. To disable it for a section of code, use
`{.push sinkInference: off.}`...`{.pop.}`.
- The compiler now supports a new switch `--panics:on` that turns runtime
@@ -261,7 +261,7 @@ echo f
([#12812](https://github.com/nim-lang/Nim/issues/12812))
- Fixed "Produce static/const initializations for variables when possible"
([#12216](https://github.com/nim-lang/Nim/issues/12216))
- Fixed "Assigning discriminator field leads to internal assert with --gc:destructors"
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
([#12821](https://github.com/nim-lang/Nim/issues/12821))
- Fixed "nimsuggest `use` command does not return all instances of symbol"
([#12832](https://github.com/nim-lang/Nim/issues/12832))

View File

@@ -1,330 +0,0 @@
# v2.0.0 - 2023-08-01
Version 2.0 is a big milestone with too many changes to list them all here.
For a full list see [details](changelog_2_0_0_details.html).
## New features
### Better tuple unpacking
Tuple unpacking for variables is now treated as syntax sugar that directly
expands into multiple assignments. Along with this, tuple unpacking for
variables can now be nested.
```nim
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
# Now nesting is supported!
let (x, (_, y), _, z) = returnsNestedTuple()
```
### Improved type inference
A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases.
For example, code like the following now compiles:
```nim
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
```
### Forbidden Tags
[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition
of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types.
For example:
```nim
type IO = object ## input/output effect
proc readLine(): string {.tags: [IO].} = discard
proc echoLine(): void = discard
proc no_IO_please() {.forbids: [IO].} =
# this is OK because it didn't define any tag:
echoLine()
# the compiler prevents this:
let y = readLine()
```
### New standard library modules
The famous `os` module got an overhaul. Several of its features are available
under a new interface that introduces a `Path` abstraction. A `Path` is
a `distinct string`, which improves the type safety when dealing with paths, files
and directories.
Use:
- `std/oserrors` for OS error reporting.
- `std/envvars` for environment variables handling.
- `std/paths` for path handling.
- `std/dirs` for directory creation/deletion/traversal.
- `std/files` for file existence checking, file deletions and moves.
- `std/symlinks` for symlink handling.
- `std/appdirs` for accessing configuration/home/temp directories.
- `std/cmdline` for reading command line parameters.
### Consistent underscore handling
The underscore identifier (`_`) is now generally not added to scope when
used as the name of a definition. While this was already the case for
variables, it is now also the case for routine parameters, generic
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
```nim
proc foo(_: int): int = _ + 1
echo foo(1)
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
echo foo[int]()
proc _() = echo "_"
_()
type _ = int
let x: _ = 3
```
Whereas the following code now compiles:
```nim
proc foo(_, _: int): int = 123
echo foo(1, 2)
proc foo[_, _](): int = 123
echo foo[int, bool]()
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
echo foo(int, bool)
proc _() = echo "one"
proc _() = echo "two"
type _ = int
type _ = float
```
### JavaScript codegen improvement
The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
JS code generation, code using these types to interface with the JS backend
may need to be updated. Note that `int` and `uint` are not affected.
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
and in the case of potential bugs with the new implementation, the
old behavior is currently still supported with the command line option
`--jsbigint64:off`.
## Docgen improvements
`Markdown` is now the default markup language of doc comments (instead
of the legacy `RstMarkdown` mode). In this release we begin to separate
RST and Markdown features to better follow specification of each
language, with the focus on Markdown development.
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
select the markup language mode in the doc comments of the current `.nim`
file for processing by `nim doc`:
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
some Pandoc Markdown features + some RST features that are missing
in our current implementation of CommonMark and Pandoc Markdown.
2. `RST` closely follows the RST spec with few additional Nim features.
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
is kept for the sake of compatibility and ease of migration.
* Added separate `md2html` and `rst2html` commands for processing
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
* Docgen now supports concise syntax for referencing Nim symbols:
instead of specifying HTML anchors directly one can use original
Nim symbol declarations (adding the aforementioned link brackets
`[...]` around them).
* To use this feature across modules, a new `importdoc` directive was added.
Using this feature for referencing also helps to ensure that links
(inside one module or the whole project) are not broken.
* Added support for RST & Markdown quote blocks (blocks starting with `>`).
* Added a popular Markdown definition lists extension.
* Added Markdown indented code blocks (blocks indented by >= 4 spaces).
* Added syntax for additional parameters to Markdown code blocks:
```nim test="nim c $1"
...
```
## C++ interop enhancements
Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize
the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types.
It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces.
Consider the following example:
```cpp
struct Base {
int someValue;
Base(int inValue) {
someValue = inValue;
};
};
class IPrinter {
public:
virtual void print() = 0;
};
```
```nim
type
Base* {.importcpp, inheritable.} = object
someValue*: int32
IPrinter* {.importcpp.} = object
const objTemplate = """
struct $1 : public $3, public IPrinter {
$2
};
""";
type NimChild {.codegenDecl: objTemplate .} = object of Base
proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} =
echo "It calls the base constructor passing " & $this.someValue
this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`.
proc print*(self: NimChild) {.virtual.} =
echo "Some value is " & $self.someValue
let child = makeNimChild(10)
child.print()
```
It outputs:
```
It calls the base constructor passing 10
Some value is 20
```
## ARC/ORC refinements
With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete:
1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable.
2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations.
3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter.
With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what?
Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference.
But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of
providing a stable ABI it is important not to lose any efficiency in the calling conventions.
## Tool changes
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
- nimgrep now offers the option `--inContext` (and `--notInContext`), which
allows to filter only matches with the context block containing a given pattern.
- nimgrep: names of options containing "include/exclude" are deprecated,
e.g. instead of `--includeFile` and `--excludeFile` we have
`--filename` and `--notFilename` respectively.
Also, the semantics are now consistent for such positive/negative filters.
- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions.
## Porting guide
### Block and Break
Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn:
```nim
block:
a()
if cond:
break
b()
```
Into:
```nim
block maybePerformB:
a()
if cond:
break maybePerformB
b()
```
### Strict funcs
The definition of `"strictFuncs"` was changed.
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
The new definition is: "A store to a ref/ptr deref is forbidden."
This new definition is much easier to understand, the price is some expressiveness. The following code used to be
accepted:
```nim
{.experimental: "strictFuncs".}
type Node = ref object
s: string
func create(s: string): Node =
result = Node()
result.s = s # store to result[]
```
Now it has to be rewritten to:
```nim
{.experimental: "strictFuncs".}
type Node = ref object
s: string
func create(s: string): Node =
result = Node(s: s)
```
### Standard library
Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them:
- `std/punycode` => `punycode`
- `std/asyncftpclient` => `asyncftpclient`
- `std/smtp` => `smtp`
- `std/db_common` => `db_connector/db_common`
- `std/db_sqlite` => `db_connector/db_sqlite`
- `std/db_mysql` => `db_connector/db_mysql`
- `std/db_postgres` => `db_connector/db_postgres`
- `std/db_odbc` => `db_connector/db_odbc`
- `std/md5` => `checksums/md5`
- `std/sha1` => `checksums/sha1`
- `std/sums` => `sums`

View File

@@ -1,560 +0,0 @@
# v2.0.0 - 2023-08-01
## Changes affecting backward compatibility
- ORC is now the default memory management strategy. Use
`--mm:refc` for a transition period.
- The `threads:on` option is now the default.
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`.
- `addr` is now available for all addressable locations,
`unsafeAddr` is now deprecated and an alias for `addr`.
- Certain definitions from the default `system` module have been moved to
the following new modules:
- `std/syncio`
- `std/assertions`
- `std/formatfloat`
- `std/objectdollar`
- `std/widestrs`
- `std/typedthreads`
- `std/sysatomics`
In the future, these definitions will be removed from the `system` module,
and their respective modules will have to be imported to use them.
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
may be used.
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
symbols in the `system` module:
- Aliases with an `Error` suffix to exception types that have a `Defect` suffix
(see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
`DeadThreadError`, `NilAccessError`
- `addQuitProc`, replaced by `exitprocs.addExitProc`
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
- `TaintedString`, formerly a distinct alias to `string`
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`.
- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore.
- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to
`pointer` anymore. `cast` may be used instead.
- The `gc:v2` option is removed.
- The `mainmodule` and `m` options are removed.
- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405))
are now opt-in via `experimental:flexibleOptionalParams`.
- Automatic dereferencing (experimental feature) is removed.
- The `Math.trunc` polyfill for targeting Internet Explorer was
previously included in most JavaScript output files.
Now, it is only included with `-d:nimJsMathTruncPolyfill`.
If you are targeting Internet Explorer, you may choose to enable this option
or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
Nim uses `Math.trunc` for the division and modulo operators for integers.
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
`sink` for optimization purposes.
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
```nim
type Foo = distinct ptr int
# Before:
var x: Foo = nil
# After:
var x: Foo = Foo(nil)
```
- Removed two type pragma syntaxes deprecated since 0.20, namely
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead,
use `type Foo[T] {.final.} = object`.
- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
This decision was made with the assumption that the old syntax was used rarely;
if your code used the old syntax, please be aware of this change.
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
are no longer experimental.
- `macros.getImpl` for `const` symbols now returns the full definition node
(as `nnkConstDef`) rather than the AST of the constant value.
- Lock levels are deprecated, now a noop.
- `strictEffects` are no longer experimental.
Use `legacy:laxEffects` to keep backward compatibility.
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`.
- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning.
This warning will become an error in future versions! Use a `cast` operation
like `cast[cstring](x)` instead.
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
- Redefining templates with the same signature was previously
allowed to support certain macro code. To do this explicitly, the
`{.redefine.}` pragma has been added. Note that this is only for templates.
Implicit redefinition of templates is now deprecated and will give an error in the future.
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
- Several Standard libraries have been moved to nimble packages, use `nimble` to install them:
- `std/punycode` => `punycode`
- `std/asyncftpclient` => `asyncftpclient`
- `std/smtp` => `smtp`
- `std/db_common` => `db_connector/db_common`
- `std/db_sqlite` => `db_connector/db_sqlite`
- `std/db_mysql` => `db_connector/db_mysql`
- `std/db_postgres` => `db_connector/db_postgres`
- `std/db_odbc` => `db_connector/db_odbc`
- `std/md5` => `checksums/md5`
- `std/sha1` => `checksums/sha1`
- `std/sums` => `std/sums`
- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning:
```nim
try:
discard
except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept]
discard
```
- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection.
- The underscore identifier (`_`) is now generally not added to scope when
used as the name of a definition. While this was already the case for
variables, it is now also the case for routine parameters, generic
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
```nim
proc foo(_: int): int = _ + 1
echo foo(1)
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
echo foo[int]()
proc _() = echo "_"
_()
type _ = int
let x: _ = 3
```
Whereas the following code now compiles:
```nim
proc foo(_, _: int): int = 123
echo foo(1, 2)
proc foo[_, _](): int = 123
echo foo[int, bool]()
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
echo foo(int, bool)
proc _() = echo "one"
proc _() = echo "two"
type _ = int
type _ = float
```
- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
JS code generation, code using these types to interface with the JS backend
may need to be updated. Note that `int` and `uint` are not affected.
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
and in the case of potential bugs with the new implementation, the
old behavior is currently still supported with the command line option
`--jsbigint64:off`.
- The `proc` and `iterator` type classes now respectively only match
procs and iterators. Previously both type classes matched any of
procs or iterators.
```nim
proc prc(): int =
123
iterator iter(): int {.closure.} =
yield 123
proc takesProc[T: proc](x: T) = discard
proc takesIter[T: iterator](x: T) = discard
# always compiled:
takesProc(prc)
takesIter(iter)
# no longer compiles:
takesProc(iter)
takesIter(prc)
```
- The `proc` and `iterator` type classes now accept a calling convention pragma
(i.e. `proc {.closure.}`) that must be shared by matching proc or iterator
types. Previously, pragmas were parsed but discarded if no parameter list
was given.
This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with
an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma
node in the same place as in a concrete `proc` or `iterator` type node. This
state of the AST may be unexpected to existing code, both due to the
replacement of the `nnkFormalParams` node as well as having child nodes
unlike other type class AST.
- Signed integer literals in `set` literals now default to a range type of
`0..255` instead of `0..65535` (the maximum size of sets).
- `case` statements with `else` branches put before `elif`/`of` branches in macros
are rejected with "invalid order of case branches".
- Destructors now default to `.raises: []` (i.e. destructors must not raise
unlisted exceptions) and explicitly raising destructors are implementation
defined behavior.
- The very old, undocumented `deprecated` pragma statement syntax for
deprecated aliases is now a no-op. The regular deprecated pragma syntax is
generally sufficient instead.
```nim
# now does nothing:
{.deprecated: [OldName: NewName].}
# instead use:
type OldName* {.deprecated: "use NewName instead".} = NewName
const oldName* {.deprecated: "use newName instead".} = newName
```
`defined(nimalias)` can be used to check for versions when this syntax was
available; however since code that used this syntax is usually very old,
these deprecated aliases are likely not used anymore and it may make sense
to simply remove these statements.
- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer
declared when they are not available on the backend. Previously it would call
`doAssert false` at runtime despite the condition being checkable at compile-time.
- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated.
- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly.
## Standard library additions and changes
[//]: # "Changes:"
- OpenSSL 3 is now supported.
- `macros.parseExpr` and `macros.parseStmt` now accept an optional
`filename` argument for more informative errors.
- The `colors` module is expanded with missing colors from the CSS color standard.
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
- The `md5` module now works at compile time and in JavaScript.
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables.
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
making limiting it to just the first char (`last = 0`) valid.
- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator.
- `random.rand` now works with `Ordinal`s.
- Undeprecated `os.isvalidfilename`.
- `std/oids` now uses `int64` to store time internally (before, it was int32).
- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`.
- `std/uri.Uri.isIpv6` is now exported.
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
- `std/jsfetch.newFetchOptions` now has default values for all parameters.
- `std/jsformdata` now accepts the `Blob` data type.
- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433).
- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall.
This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
```sh
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
$ sh build.sh # per unix install instructions
$ bin/nim c koch # per unix install instructions
$ ./koch boot -d:release # per unix install instructions
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
```
This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular).
[//]: # "Additions:"
- Added ISO 8601 week date utilities in `times`:
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
- Added an `initDateTime` overload to create a `DateTime` from an ISO week date.
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
- Added new modules which were previously part of `std/os`:
- Added `std/oserrors` for OS error reporting.
- Added `std/envvars` for environment variables handling.
- Added `std/cmdline` for reading command line parameters.
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
- Added `sep` parameter in `std/uri` to specify the query separator.
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
- Added `complex.sgn` for obtaining the phase of complex numbers.
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
`toggleAttribute`, and `matches` to `std/dom`.
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices).
- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460).
- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse.
- Added a `safe` parameter to `base64.encodeMime`.
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences.
- `std/jscore` for the JavaScript target:
+ Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask).
+ Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`.
- Added `BackwardsIndex` overload for `CacheSeq`.
- Added support for nested `with` blocks in `std/with`.
- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time.
[//]: # "Deprecations:"
- Deprecated `selfExe` for Nimscript.
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
Now only `byte` and `char` are supported.
[//]: # "Removals:"
- Removed deprecated module `parseopt2`.
- Removed deprecated module `sharedstrings`.
- Removed deprecated module `dom_extensions`.
- Removed deprecated module `LockFreeHash`.
- Removed deprecated module `events`.
- Removed deprecated `oids.oidToString`.
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
- Removed deprecated `jsre.test` and `jsre.toString`.
- Removed deprecated `math.c_frexp`.
- Removed deprecated `` httpcore.`==` ``.
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types.
- Removed deprecated `osproc.poDemon`, symbol with typo.
- Removed deprecated `tables.rightSize`.
- Removed deprecated `posix.CLONE_STOPPED`.
## Language changes
- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma
which can be used to disable certain effects in proc types.
- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
for the right-hand side of type definitions in type sections. Previously
they would error with "invalid indentation".
- Compile-time define changes:
- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
In the command line, this is defined as `-d:a.b.c`. Older versions can
use backticks as in ``defined(`a.b.c`)`` to access such defines.
- [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
now support a string argument for qualified define names.
```nim
# -d:package.FooBar=42
const FooBar {.intdefine: "package.FooBar".}: int = 5
echo FooBar # 42
```
This was added to help disambiguate similar define names for different packages.
In older versions, this could only be achieved with something like the following:
```nim
const FooBar = block:
const `package.FooBar` {.intdefine.}: int = 5
`package.FooBar`
```
- A generic `define` pragma for constants has been added that interprets
the value of the define based on the type of the constant value.
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma)
for a list of supported types.
- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
- Templates now accept macro pragmas.
- Macro pragmas for var/let/const sections have been redesigned in a way that works
similarly to routine macro pragmas. The new behavior is documented in the
[experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
allowing multiple type definitions to be injected in place of the original type definition.
```nim
import macros
macro multiply(amount: static int, s: untyped): untyped =
let name = $s[0].basename
result = newNimNode(nnkTypeSection)
for i in 1 .. amount:
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
type
Foo = object
Bar {.multiply: 3.} = object
x, y, z: int
Baz = object
# becomes
type
Foo = object
Bar1 = object
x, y, z: int
Bar2 = object
x, y, z: int
Bar3 = object
x, y, z: int
Baz = object
```
- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
has been implemented for a variety of basic cases. For example, code like the following now compiles:
```nim
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
```
- `cstring` is now accepted as a selector in `case` statements, removing the
need to convert to `string`. On the JS backend, this is translated directly
to a `switch` statement.
- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
- IBM Z architecture and macOS m1 arm64 architecture are supported.
- `=wasMoved` can now be overridden by users.
- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code
generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care.
- Tuple unpacking for variables is now treated as syntax sugar that directly
expands into multiple assignments. Along with this, tuple unpacking for
variables can now be nested.
```nim
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
let (x, (_, y), _, z) = returnsNestedTuple()
# roughly becomes
let
tmpTup1 = returnsNestedTuple()
x = tmpTup1[0]
tmpTup2 = tmpTup1[1]
y = tmpTup2[1]
z = tmpTup1[3]
```
As a result `nnkVarTuple` nodes in variable sections will no longer be
reflected in `typed` AST.
- C++ interoperability:
- New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added.
- Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
## Compiler changes
- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
reality better. (Nim moved away from all techniques based on "tracing".)
- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
without requiring `-d:nimVersion140` which is now a noop.
- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
related functions produced on the backend. This prevents conflicts with other Nim
static libraries.
- When compiling for release, the flag `-fno-math-errno` is used for GCC.
- Removed deprecated `LineTooLong` hint.
- Line numbers and file names of source files work correctly inside templates for JavaScript targets.
- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers.
## Docgen
- `Markdown` is now the default markup language of doc comments (instead
of the legacy `RstMarkdown` mode). In this release we begin to separate
RST and Markdown features to better follow specification of each
language, with the focus on Markdown development.
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
select the markup language mode in the doc comments of the current `.nim`
file for processing by `nim doc`:
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
some Pandoc Markdown features + some RST features that are missing
in our current implementation of CommonMark and Pandoc Markdown.
2. `RST` closely follows the RST spec with few additional Nim features.
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
is kept for the sake of compatibility and ease of migration.
* Added separate `md2html` and `rst2html` commands for processing
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
- Docgen now supports concise syntax for referencing Nim symbols:
instead of specifying HTML anchors directly one can use original
Nim symbol declarations (adding the aforementioned link brackets
`[...]` around them).
* To use this feature across modules, a new `importdoc` directive was added.
Using this feature for referencing also helps to ensure that links
(inside one module or the whole project) are not broken.
- Added support for RST & Markdown quote blocks (blocks starting with `>`).
- Added a popular Markdown definition lists extension.
- Added Markdown indented code blocks (blocks indented by >= 4 spaces).
- Added syntax for additional parameters to Markdown code blocks:
```nim test="nim c $1"
...
```
## Tool changes
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
- nimgrep added the option `--inContext` (and `--notInContext`), which
allows to filter only matches with the context block containing a given pattern.
- nimgrep: names of options containing "include/exclude" are deprecated,
e.g. instead of `--includeFile` and `--excludeFile` we have
`--filename` and `--notFilename` respectively.
Also the semantics are now consistent for such positive/negative filters.
- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice.

View File

@@ -1,248 +0,0 @@
# v2.2.0 - 2024-10-02
## Changes affecting backward compatibility
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
```nim
var a, b: int
(a, b) = (1, 2, 3, 4)
```
will no longer compile.
- `internalNew` is removed from the `system` module, use `new` instead.
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
- The JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
- The JS backend now supports closure iterators.
- `owner` in `std/macros` is deprecated.
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
Previously; in templates they would error immediately at the template definition,
and in generic procs a type symbol would arbitrarily be captured, losing the
information of the other symbols. This means that generic code can now give
errors for ambiguous type symbols, and macros operating on generic proc AST
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
- Partial generic instantiation of routines is no longer allowed. Previously
it compiled in niche situations due to bugs in the compiler.
```nim
proc foo[T, U](x: T, y: U) = echo (x, y)
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
proc bar[T]() =
foo[float](1, "abc")
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
```
- `const` values now open a new scope for each constant, meaning symbols
declared in them can no longer be used outside or in the value of
other constants.
```nim
const foo = (var a = 1; a)
const bar = a # error
let baz = a # error
```
- The following POSIX wrappers have had their types changed from signed to
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
- `Gid` (was `int32`, is now `uint32`)
- `Uid` (was `int32`, is now `uint32`)
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
(were `int32`, are now `uint32`)
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
## Standard library additions and changes
[//]: # "Changes:"
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
[//]: # "Additions:"
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `setLenUninit` to the `system` module, which doesn't initialize
slots when enlarging a sequence.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
convert a value with a range type to its base type.
- Added Viewport API for the JavaScript targets in the `dom` module.
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
instead of `--mm:orc`.
- A `$` template is provided for `Path` in `std/paths`.
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
on Google's Farm Hash) which is also often faster than the present one. Define
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
produces the old values. This may impact your automated tests if they depend
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
is often an easy workaround.
[//]: # "Deprecations:"
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
[//]: # "Removals:"
## Language changes
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
- `member` can be used to attach a procedure to a C++ type.
- Inside a C++ constructor, `result` can be used to access the created object rather than `this`.
- Tuple unpacking changes:
- Tuple unpacking assignment now supports using underscores to discard values.
```nim
var a, c: int
(a, _, c) = (1, 2, 3)
```
- Tuple unpacking variable declarations now support type annotations, but
only for the entire tuple.
```nim
let (a, b): (int, int) = (1, 2)
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
```
- The experimental option `--experimental:openSym` has been added to allow
captured symbols in generic routine and template bodies respectively to be
replaced by symbols injected locally by templates/macros at instantiation
time. `bind` may be used to keep the captured symbols over the injected ones
regardless of enabling the option, but other methods like renaming the
captured symbols should be used instead so that the code is not affected by
context changes.
Since this change may affect runtime behavior, the experimental switch
`openSym` needs to be enabled; and a warning is given in the case where an
injected symbol would replace a captured symbol not bound by `bind` and
the experimental switch isn't enabled.
```nim
const value = "captured"
template foo(x: int, body: untyped): untyped =
let value {.inject.} = "injected"
body
proc old[T](): string =
foo(123):
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
echo old[int]() # "captured"
template oldTempl(): string =
block:
foo(123):
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
echo oldTempl() # "captured"
{.experimental: "openSym".}
proc bar[T](): string =
foo(123):
return value
assert bar[int]() == "injected" # previously it would be "captured"
proc baz[T](): string =
bind value
foo(123):
return value
assert baz[int]() == "captured"
template barTempl(): string =
block:
foo(123):
value
assert barTempl() == "injected" # previously it would be "captured"
template bazTempl(): string =
bind value
block:
foo(123):
value
assert bazTempl() == "captured"
```
This option also generates a new node kind `nnkOpenSym` which contains
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
modified `nnkOpenSymChoice` node but macros that want to support the
experimental feature should still handle `nnkOpenSym`, as the node kind would
simply not be generated as opposed to being removed.
Another experimental switch `genericsOpenSym` exists that enables this behavior
at instantiation time, meaning templates etc can enable it specifically when
they are being called. However this does not generate `nnkOpenSym` nodes
(unless the other switch is enabled) and so doesn't reflect the regular
behavior of the switch.
```nim
const value = "captured"
template foo(x: int, body: untyped): untyped =
let value {.inject.} = "injected"
{.push experimental: "genericsOpenSym".}
body
{.pop.}
proc bar[T](): string =
foo(123):
return value
echo bar[int]() # "injected"
template barTempl(): string =
block:
var res: string
foo(123):
res = value
res
assert barTempl() == "injected"
```
## Compiler changes
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
## Tool changes
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.

View File

@@ -1,4 +1,4 @@
# v2.xx.x - yyyy-mm-dd
# v1.xx.x - yyyy-mm-dd
This is an example file.
The changes should go to changelog.md!

View File

@@ -3,7 +3,7 @@ import std/[strutils, os, osproc, parseutils, strformat]
proc main() =
var msg = ""
const cmd = "./koch boot --mm:orc -d:release"
const cmd = "./koch boot --gc:orc -d:release"
let (output, exitCode) = execCmdEx(cmd)

View File

@@ -58,11 +58,7 @@ _nimNumCpu(){
# FreeBSD | macOS: $(sysctl -n hw.ncpu)
# OpenBSD: $(sysctl -n hw.ncpuonline)
# windows: $NUMBER_OF_PROCESSORS ?
if env | grep -q '^NIMCORES='; then
echo $NIMCORES
else
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
fi
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
}
_nimBuildCsourcesIfNeeded(){

View File

@@ -74,7 +74,7 @@ proc aliases*(obj, field: PNode): AliasKind =
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
template collectImportantNodes(result, n) =
var result: seq[PNode] = @[]
var result: seq[PNode]
var n = n
while true:
case n.kind

View File

@@ -10,9 +10,7 @@
## Simple alias analysis for the HLO and the code generators.
import
ast, astalgo, types, trees
import std/intsets
ast, astalgo, types, trees, intsets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -51,16 +49,14 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind
of tyObject:
if a.baseClass != nil:
result = isPartOfAux(a.baseClass.skipTypes(skipPtrs), b, marker)
if a[0] != nil:
result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = isPartOfAux(skipModifier(a), b, marker)
of tySet, tyArray:
result = isPartOfAux(a.elementType, b, marker)
of tyTuple:
for aa in a.kids:
result = isPartOfAux(aa, b, marker)
result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tySet, tyTuple:
for i in 0..<a.len:
result = isPartOfAux(a[i], b, marker)
if result == arYes: return
else: discard
@@ -118,8 +114,6 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# use expensive type check:
if isPartOf(a.sym.typ, b.sym.typ) != arNo:
result = arMaybe
else:
result = arNo
of nkBracketExpr:
result = isPartOf(a[0], b[0])
if a.len >= 2 and b.len >= 2:
@@ -155,7 +149,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
result = isPartOf(a[1], b[1])
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isPartOf(a[0], b[0])
else: result = arNo
else: discard
# Calls return a new location, so a default of ``arNo`` is fine.
else:
# go down recursively; this is quite demanding:
@@ -171,7 +165,6 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
of DerefKinds:
# a* !<| b[] iff
result = arNo
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a, b[0])
if result == arNo: result = arMaybe
@@ -193,9 +186,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a[0], b)
if result == arNo: result = arMaybe
else:
result = arNo
else: result = arNo
else: discard
of nkObjConstr:
result = arNo
for i in 1..<b.len:
@@ -213,6 +204,4 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
of nkBracket:
if b.len > 0:
result = isPartOf(a, b[0])
else:
result = arNo
else: result = arNo
else: discard

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,18 +12,26 @@
# the data structures here are used in various places of the compiler.
import
ast, astyaml, options, lineinfos, idents, rodutils,
ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
msgs
import std/[hashes, intsets]
import std/strutils except addf
export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr
import strutils except addf
when defined(nimPreviewSlimSystem):
import std/assertions
when not defined(nimHasCursor):
{.pragma: cursor.}
proc hashNode*(p: RootRef): Hash
proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes.
# If maxRecDepht <> -1 then it won't print the whole graph.
proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
# these are for debugging only: They are not really deprecated, but I want
# the warning so that release versions do not contain debugging statements:
@@ -31,6 +39,15 @@ proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
proc typekinds*(t: PType) {.deprecated.} =
var t = t
var s = ""
while t != nil and t.len > 0:
s.add $t.kind
s.add " "
t = t.lastSon
echo s
template debug*(x: PSym|PType|PNode) {.deprecated.} =
when compiles(c.config):
debug(c.config, x)
@@ -65,9 +82,21 @@ template mdbg*: bool {.deprecated.} =
else:
error()
# --------------------------- ident tables ----------------------------------
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
proc idTableGet*(t: TIdTable, key: int): RootRef
proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef)
proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
# checks if `t` contains the `key` (compared by the pointer value, not only
# `key`'s id)
proc idNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
# ---------------------------------------------------------------------------
proc lookupInRecord*(n: PNode, field: PIdent): PSym
proc mustRehash*(length, counter: int): bool
proc nextTry*(h, maxHash: Hash): Hash {.inline.}
# ------------- table[int, int] ---------------------------------------------
const
@@ -83,7 +112,7 @@ type
data*: TIIPairSeq
proc initIITable*(x: var TIITable)
proc initIiTable*(x: var TIITable)
proc iiTableGet*(t: TIITable, key: int): int
proc iiTablePut*(t: var TIITable, key, val: int)
@@ -171,7 +200,6 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
result = nil
proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
result = false
if a[0] != b[0]: return false
var alen = a.len - 1
while alen > 0 and a[alen] != '`': dec(alen)
@@ -201,11 +229,10 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
## Named parameters are special because a named parameter can be
## gensym'ed and then they have '\`<number>' suffix that we need to
## ignore, see compiler / evaltempl.nim, snippet:
## ```nim
## ```
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
## if c.instLines: actual.info else: templ.info)
## ```
result = nil
for i in 1..<list.len:
let it = list[i].sym
if it.name.id == ident.id or
@@ -214,7 +241,173 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
proc hashNode(p: RootRef): Hash =
result = hash(cast[pointer](p))
import std/tables
proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc rspaces(x: int): Rope =
# returns x spaces
result = rope(spaces(x))
proc toYamlChar(c: char): string =
case c
of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
of '\'', '\"', '\\': result = '\\' & c
else: result = $c
proc makeYamlString*(s: string): Rope =
# We have to split long strings into many ropes. Otherwise
# this could trigger InternalError(111). See the ropes module for
# further information.
const MaxLineLength = 64
result = ""
var res = "\""
for i in 0..<s.len:
if (i + 1) mod MaxLineLength == 0:
res.add('\"')
res.add("\n")
result.add(rope(res))
res = "\"" # reset
res.add(toYamlChar(s[i]))
res.add('\"')
result.add(rope(res))
proc flagsToStr[T](flags: set[T]): Rope =
if flags == {}:
result = rope("[]")
else:
result = ""
for x in items(flags):
if result != "": result.add(", ")
result.add(makeYamlString($x))
result = "[" & result & "]"
proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
rope(toLinenumber(info)),
rope(toColumn(info))]
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
indent, maxRecDepth: int): Rope
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
elif containsOrIncl(marker, n.id):
result = "\"$1\"" % [rope(n.name.s)]
else:
var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
#rope("typ"), typeToYamlAux(conf, n.typ, marker,
# indent + 2, maxRecDepth - 1),
let istr = rspaces(indent + 2)
result = rope("{")
result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
result.addf("$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
result.addf("$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
if conf != nil:
# if we don't pass the config, we probably don't care about the line info
result.addf("$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
if card(n.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
result.addf("$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
result.addf("$N$1\"ast\": $2", [istr, ast])
result.addf("$N$1\"options\": $2", [istr, flagsToStr(n.options)])
result.addf("$N$1\"position\": $2", [istr, rope(n.position)])
result.addf("$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
result.addf("$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
result.addf("$N$1\"r\": $2", [istr, n.loc.r])
result.addf("$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
result.addf("$N$1}", [rspaces(indent)])
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
var sonsRope: Rope
if n == nil:
sonsRope = rope("null")
elif containsOrIncl(marker, n.id):
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
else:
if n.len > 0:
sonsRope = rope("[")
for i in 0..<n.len:
if i > 0: sonsRope.add(",")
sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i],
marker, indent + 4, maxRecDepth - 1)])
sonsRope.addf("$N$1]", [rspaces(indent + 2)])
else:
sonsRope = rope("null")
let istr = rspaces(indent + 2)
result = rope("{")
result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
result.addf("$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
result.addf("$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
if card(n.flags) > 0:
result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)])
result.addf("$N$1\"size\": $2", [istr, rope(n.size)])
result.addf("$N$1\"align\": $2", [istr, rope(n.align)])
result.addf("$N$1\"sons\": $2", [istr, sonsRope])
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
maxRecDepth: int): Rope =
if n == nil:
result = rope("null")
else:
var istr = rspaces(indent + 2)
result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
if maxRecDepth != 0:
if conf != nil:
result.addf(",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
case n.kind
of nkCharLit..nkInt64Lit:
result.addf(",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
result.addf(",$N$1\"floatVal\": $2",
[istr, rope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
result.addf(",$N$1\"sym\": $2",
[istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
of nkIdent:
if n.ident != nil:
result.addf(",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
else:
result.addf(",$N$1\"ident\": null", [istr])
else:
if n.len > 0:
result.addf(",$N$1\"sons\": [", [istr])
for i in 0..<n.len:
if i > 0: result.add(",")
result.addf("$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n[i],
marker, indent + 4, maxRecDepth - 1)])
result.addf("$N$1]", [istr])
result.addf(",$N$1\"typ\": $2",
[istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
result.addf("$N$1}", [rspaces(indent)])
proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
var marker = initIntSet()
result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
import tables
const backrefStyle = "\e[90m"
const enumStyle = "\e[34m"
@@ -378,12 +571,14 @@ proc value(this: var DebugPrinter; value: PType) =
this.key "n"
this.value value.n
this.key "sons"
this.openBracket
for i, a in value.ikids:
if i > 0: this.comma
this.value a
this.closeBracket
if value.len > 0:
this.key "sons"
this.openBracket
for i in 0..<value.len:
this.value value[i]
if i != value.len - 1:
this.comma
this.closeBracket
if value.n != nil:
this.key "n"
@@ -452,32 +647,35 @@ proc value(this: var DebugPrinter; value: PNode) =
proc debug(n: PSym; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: true,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc debug(n: PType; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: true,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc debug(n: PNode; conf: ConfigRef) =
var this = DebugPrinter(
visited: initTable[pointer, int](),
renderSymType: false,
useColor: not defined(windows)
)
var this: DebugPrinter
this.visited = initTable[pointer, int]()
#this.renderSymType = true
this.useColor = not defined(windows)
this.value(n)
echo($this.res)
proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
proc objectSetContains*(t: TObjectSet, obj: RootRef): bool =
# returns true whether n is in t
var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
@@ -525,6 +723,95 @@ proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool =
inc(t.counter)
result = false
proc strTableContains*(t: TStrTable, n: PSym): bool =
var h: Hash = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil:
if (t.data[h] == n):
return true
h = nextTry(h, high(t.data))
result = false
proc strTableRawInsert(data: var seq[PSym], n: PSym) =
var h: Hash = n.name.h and high(data)
while data[h] != nil:
if data[h] == n:
# allowed for 'export' feature:
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
return
h = nextTry(h, high(data))
assert(data[h] == nil)
data[h] = n
proc symTabReplaceRaw(data: var seq[PSym], prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h
var h: Hash = prevSym.name.h and high(data)
while data[h] != nil:
if data[h] == prevSym:
data[h] = newSym
return
h = nextTry(h, high(data))
assert false
proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) =
symTabReplaceRaw(t.data, prevSym, newSym)
proc strTableEnlarge(t: var TStrTable) =
var n: seq[PSym]
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if t.data[i] != nil: strTableRawInsert(n, t.data[i])
swap(t.data, n)
proc strTableAdd*(t: var TStrTable, n: PSym) =
if mustRehash(t.data.len, t.counter): strTableEnlarge(t)
strTableRawInsert(t.data, n)
inc(t.counter)
proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
onConflictKeepOld = false): PSym =
# if `t` has a conflicting symbol (same identifier as `n`), return it
# otherwise return `nil`. Incl `n` to `t` unless `onConflictKeepOld = true`
# and a conflict was found.
assert n.name != nil
var h: Hash = n.name.h and high(t.data)
var replaceSlot = -1
while true:
var it = t.data[h]
if it == nil: break
# Semantic checking can happen multiple times thanks to templates
# and overloading: (var x=@[]; x).mapIt(it).
# So it is possible the very same sym is added multiple
# times to the symbol table which we allow here with the 'it == n' check.
if it.name.id == n.name.id:
if it == n: return nil
replaceSlot = h
h = nextTry(h, high(t.data))
if replaceSlot >= 0:
result = t.data[replaceSlot] # found it
if not onConflictKeepOld:
t.data[replaceSlot] = n # overwrite it with newer definition!
return result # but return the old one
elif mustRehash(t.data.len, t.counter):
strTableEnlarge(t)
strTableRawInsert(t.data, n)
else:
assert(t.data[h] == nil)
t.data[h] = n
inc(t.counter)
result = nil
proc strTableIncl*(t: var TStrTable, n: PSym;
onConflictKeepOld = false): bool {.discardable.} =
result = strTableInclReportConflict(t, n, onConflictKeepOld) != nil
proc strTableGet*(t: TStrTable, name: PIdent): PSym =
var h: Hash = name.h and high(t.data)
while true:
result = t.data[h]
if result == nil: break
if result.name.id == name.id: break
h = nextTry(h, high(t.data))
type
TIdentIter* = object # iterator over all syms with same identifier
@@ -606,75 +893,124 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
result = nextIter(ti, tab)
iterator items*(tab: TStrTable): PSym =
var it: TTabIter = default(TTabIter)
var it: TTabIter
var s = initTabIter(it, tab)
while s != nil:
yield s
s = nextIter(it, tab)
proc isNil(x: ItemId): bool {.inline.} =
x.module == 0 and x.item == 0
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
proc hasEmptySlot(data: TIdPairSeq): bool =
for h in 0..high(data):
if isNil(data[h].key):
if data[h].key == nil:
return true
result = false
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
proc idTableRawGet(t: TIdTable, key: int): int =
var h: Hash
h = key and high(t.data) # start with real hash value
while not isNil(t.data[h].key):
if toId(t.data[h].key) == key:
while t.data[h].key != nil:
if t.data[h].key.id == key:
return h
h = nextTry(h, high(t.data))
result = - 1
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
var index = idTableRawGet(t, toId(key))
proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
var index = idTableRawGet(t, key.id)
if index >= 0: result = t.data[index].key == key
else: result = false
proc idTableGet(t: TIdTable, key: PIdObj): RootRef =
var index = idTableRawGet(t, key.id)
if index >= 0: result = t.data[index].val
else: result = default(T)
else: result = nil
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
getOrDefault(t, key.itemId)
proc idTableGet(t: TIdTable, key: int): RootRef =
var index = idTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
for i in 0..high(t.data):
if t.data[i].key != nil:
yield (t.data[i].key.id, t.data[i].val)
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
var h: Hash
let keyId = toId(key)
h = keyId and high(data)
while not isNil(data[h].key):
assert(toId(data[h].key) != keyId)
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(isNil(data[h].key))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
var
index: int
n: TIdPairSeq[T]
index = idTableRawGet(t, toId(key))
n: TIdPairSeq
index = idTableRawGet(t, key.id)
if index >= 0:
assert(not isNil(t.data[index].key))
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if not isNil(t.data[i].key):
if t.data[i].key != nil:
idTableRawInsert(n, t.data[i].key, t.data[i].val)
assert(hasEmptySlot(n))
swap(t.data, n)
idTableRawInsert(t.data, key, val)
inc(t.counter)
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
t[key.itemId] = val
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
for i in 0..high(t.data):
if not isNil(t.data[i].key):
yield (t.data[i].key, t.data[i].val)
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: Hash
h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key.id == key.id:
return h
h = nextTry(h, high(t.data))
result = - 1
proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
var index: int
index = idNodeTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: Hash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].key = key
data[h].val = val
proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
var index = idNodeTableRawGet(t, key)
if index >= 0:
assert(t.data[index].key != nil)
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
var n: TIdNodePairSeq
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if t.data[i].key != nil:
idNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
swap(t.data, n)
idNodeTableRawInsert(t.data, key, val)
inc(t.counter)
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
for i in 0..high(t.data):
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc initIITable(x: var TIITable) =
x.counter = 0
@@ -721,8 +1057,15 @@ proc iiTablePut(t: var TIITable, key, val: int) =
iiTableRawInsert(t.data, key, val)
inc(t.counter)
proc isAddrNode*(n: PNode): bool =
case n.kind
of nkAddr, nkHiddenAddr: true
of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
else: false
else: false
proc listSymbolNames*(symbols: openArray[PSym]): string =
result = ""
for sym in symbols:
if result.len > 0:
result.add ", "

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ import options, ast, msgs
proc typSym*(t: PType): PSym =
result = t.sym
if result == nil and t.kind == tyGenericInst: # this might need to be refined
result = t.genericHead.sym
result = t[0].sym
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
@@ -24,12 +24,6 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
result.add "]"
proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
result.add " [$1" % typ.kind.toHumanStr
if typ.sym != nil:
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
result.add "]"
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)

View File

@@ -1,160 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# AST YAML printing
import "."/[ast, lineinfos, msgs, options, rodutils]
import std/[intsets, strutils]
proc addYamlString*(res: var string; s: string) =
res.add "\""
for c in s:
case c
of '\0' .. '\x1F', '\x7F' .. '\xFF':
res.add("\\u" & strutils.toHex(ord(c), 4))
of '\"', '\\':
res.add '\\' & c
else:
res.add c
res.add('\"')
proc makeYamlString(s: string): string =
result = ""
result.addYamlString(s)
proc flagsToStr[T](flags: set[T]): string =
if flags == {}:
result = "[]"
else:
result = ""
for x in items(flags):
if result != "":
result.add(", ")
result.addYamlString($x)
result = "[" & result & "]"
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
result = "["
result.addYamlString(toFilename(conf, info))
result.addf ", $1, $2]", toLinenumber(info), toColumn(info)
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, indent, maxRecDepth: int)
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
if n == nil:
res.add("null")
elif containsOrIncl(marker, n.id):
res.addYamlString(n.name.s)
else:
let istr = spaces(indent * 4)
if nl:
res.addf("\n$1", istr)
res.addf("kind: $1", [makeYamlString($n.kind)])
res.addf("\n$1name: $2", [istr, makeYamlString(n.name.s)])
res.addf("\n$1typ: ", [istr])
res.typeToYamlAux(conf, n.typ, marker, true, indent + 1, maxRecDepth - 1)
if conf != nil:
# if we don't pass the config, we probably don't care about the line info
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
if card(n.flags) > 0:
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
res.addf("\n$1magic: $2", [istr, makeYamlString($n.magic)])
res.addf("\n$1ast: ", [istr])
res.treeToYamlAux(conf, n.ast, marker, true, indent + 1, maxRecDepth - 1)
res.addf("\n$1options: $2", [istr, flagsToStr(n.options)])
res.addf("\n$1position: $2", [istr, $n.position])
res.addf("\n$1k: $2", [istr, makeYamlString($n.loc.k)])
res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
if card(n.loc.flags) > 0:
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
res.addf("\n$1lode: ", [istr])
res.treeToYamlAux(conf, n.loc.lode, marker, true, indent + 1, maxRecDepth - 1)
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; nl: bool, indent: int; maxRecDepth: int) =
if n == nil:
res.add("null")
elif containsOrIncl(marker, n.id):
res.addf "\"$1 @$2\"" % [$n.kind, strutils.toHex(cast[uint](n), sizeof(n) * 2)]
else:
let istr = spaces(indent * 4)
if nl:
res.addf("\n$1", istr)
res.addf("kind: $2", [istr, makeYamlString($n.kind)])
res.addf("\n$1sym: ", istr)
res.symToYamlAux(conf, n.sym, marker, true, indent + 1, maxRecDepth - 1)
res.addf("\n$1n: ", istr)
res.treeToYamlAux(conf, n.n, marker, true, indent + 1, maxRecDepth - 1)
if card(n.flags) > 0:
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)])
res.addf("\n$1size: $2", [istr, $(n.size)])
res.addf("\n$1align: $2", [istr, $(n.align)])
if n.hasElementType:
res.addf("\n$1sons:", istr)
for a in n.kids:
res.addf("\n$1 - ", istr)
res.typeToYamlAux(conf, a, marker, false, indent + 1, maxRecDepth - 1)
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; nl: bool, indent: int;
maxRecDepth: int) =
if n == nil:
res.add("null")
else:
var istr = spaces(indent * 4)
if nl:
res.addf("\n$1", istr)
res.addf("kind: $1" % [makeYamlString($n.kind)])
if maxRecDepth != 0:
if conf != nil:
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
case n.kind
of nkCharLit .. nkUInt64Lit:
res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
of nkFloatLit .. nkFloat128Lit:
res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision])
of nkStrLit .. nkTripleStrLit:
res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)])
of nkSym:
res.addf("\n$1sym: ", [istr])
res.symToYamlAux(conf, n.sym, marker, true, indent + 1, maxRecDepth)
of nkIdent:
if n.ident != nil:
res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
else:
res.addf("\n$1ident: null", [istr])
else:
if n.len > 0:
res.addf("\n$1sons: ", [istr])
for i in 0 ..< n.len:
res.addf("\n$1 - ", [istr])
res.treeToYamlAux(conf, n[i], marker, false, indent + 1, maxRecDepth - 1)
if n.typ != nil:
res.addf("\n$1typ: ", [istr])
res.typeToYamlAux(conf, n.typ, marker, true, indent + 1, maxRecDepth)
proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string =
var marker = initIntSet()
result = newStringOfCap(1024)
result.treeToYamlAux(conf, n, marker, false, indent, maxRecDepth)
proc typeToYaml*(conf: ConfigRef; n: PType; indent: int = 0; maxRecDepth: int = -1): string =
var marker = initIntSet()
result = newStringOfCap(1024)
result.typeToYamlAux(conf, n, marker, false, indent, maxRecDepth)
proc symToYaml*(conf: ConfigRef; n: PSym; indent: int = 0; maxRecDepth: int = -1): string =
var marker = initIntSet()
result = newStringOfCap(1024)
result.symToYamlAux(conf, n, marker, false, indent, maxRecDepth)

View File

@@ -87,11 +87,5 @@ const populationCount: array[uint8, uint8] = block:
arr
proc bitSetCard*(x: TBitSet): BiggestInt =
result = 0
for it in x:
result.inc int(populationCount[it])
proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt =
result = 0
for j in 0..<size:
if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))

View File

@@ -38,7 +38,6 @@ template less(a, b): bool = cmp(a, b) < 0
template eq(a, b): bool = cmp(a, b) == 0
proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
result = default(Val)
var x = b.root
while x.isInternal:
for j in 0..<x.entries:
@@ -69,7 +68,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
result.links[j] = h.links[Mhalf + j]
else:
for j in 0..<Mhalf:
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc):
result.vals[j] = move h.vals[Mhalf + j]
else:
shallowCopy(result.vals[j], h.vals[Mhalf + j])
@@ -92,7 +91,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
if less(key, h.keys[j]): break
inc j
for i in countdown(h.entries, j+1):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc):
h.vals[i] = move h.vals[i-1]
else:
shallowCopy(h.vals[i], h.vals[i-1])

View File

@@ -1,164 +0,0 @@
import ropes, int128
type
Snippet* = string
Builder* = object
buf*: string
indents*: int
template newBuilder*(s: string): Builder =
Builder(buf: s)
proc extract*(builder: Builder): Snippet =
builder.buf
proc add*(builder: var Builder, s: string) =
builder.buf.add(s)
proc add*(builder: var Builder, s: char) =
builder.buf.add(s)
proc addNewline*(builder: var Builder) =
builder.add('\n')
for i in 0 ..< builder.indents:
builder.add('\t')
proc addLineEnd*(builder: var Builder, s: string) =
builder.add(s)
builder.addNewline()
proc addLineEndIndent*(builder: var Builder, s: string) =
inc builder.indents
builder.add(s)
builder.addNewline()
proc addDedent*(builder: var Builder, s: string) =
if builder.buf.len > 0 and builder.buf[^1] == '\t':
builder.buf.setLen(builder.buf.len - 1)
builder.add(s)
dec builder.indents
proc addLineEndDedent*(builder: var Builder, s: string) =
builder.addDedent(s)
builder.addNewline()
proc addLineComment*(builder: var Builder, comment: string) =
# probably no-op on nifc
builder.add("// ")
builder.add(comment)
builder.addNewline()
proc addIntValue*(builder: var Builder, val: int) =
builder.buf.addInt(val)
proc addIntValue*(builder: var Builder, val: int64) =
builder.buf.addInt(val)
proc addIntValue*(builder: var Builder, val: uint64) =
builder.buf.addInt(val)
proc addIntValue*(builder: var Builder, val: Int128) =
builder.buf.addInt128(val)
template cIntValue*(val: int): Snippet = $val
template cIntValue*(val: int64): Snippet = $val
template cIntValue*(val: uint64): Snippet = $val
template cIntValue*(val: Int128): Snippet = $val
template cUintValue*(val: uint): Snippet = $val & "U"
import std/formatfloat
proc addFloatValue*(builder: var Builder, val: float) =
builder.buf.addFloat(val)
template cFloatValue*(val: float): Snippet = $val
proc addInt64Literal*(result: var Builder; i: BiggestInt) =
if i > low(int64):
result.add "IL64($1)" % [rope(i)]
else:
result.add "(IL64(-9223372036854775807) - IL64(1))"
proc addUint64Literal*(result: var Builder; i: uint64) =
result.add rope($i & "ULL")
proc addIntLiteral*(result: var Builder; i: BiggestInt) =
if i > low(int32) and i <= high(int32):
result.addIntValue(i)
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
result.add "(-2147483647 -1)"
elif i > low(int64):
result.add "IL64($1)" % [rope(i)]
else:
result.add "(IL64(-9223372036854775807) - IL64(1))"
proc addIntLiteral*(result: var Builder; i: Int128) =
addIntLiteral(result, toInt64(i))
proc cInt64Literal*(i: BiggestInt): Snippet =
if i > low(int64):
result = "IL64($1)" % [rope(i)]
else:
result = "(IL64(-9223372036854775807) - IL64(1))"
proc cUint64Literal*(i: uint64): Snippet =
result = $i & "ULL"
proc cIntLiteral*(i: BiggestInt): Snippet =
if i > low(int32) and i <= high(int32):
result = rope(i)
elif i == low(int32):
# Nim has the same bug for the same reasons :-)
result = "(-2147483647 -1)"
elif i > low(int64):
result = "IL64($1)" % [rope(i)]
else:
result = "(IL64(-9223372036854775807) - IL64(1))"
proc cIntLiteral*(i: Int128): Snippet =
result = cIntLiteral(toInt64(i))
const
NimInt* = "NI"
NimInt8* = "NI8"
NimInt16* = "NI16"
NimInt32* = "NI32"
NimInt64* = "NI64"
CInt* = "int"
NimUint* = "NU"
NimUint8* = "NU8"
NimUint16* = "NU16"
NimUint32* = "NU32"
NimUint64* = "NU64"
NimFloat* = "NF"
NimFloat32* = "NF32"
NimFloat64* = "NF64"
NimFloat128* = "NF128" # not actually defined
NimNan* = "NAN"
NimInf* = "INF"
NimBool* = "NIM_BOOL"
NimTrue* = "NIM_TRUE"
NimFalse* = "NIM_FALSE"
NimChar* = "NIM_CHAR"
CChar* = "char"
NimCstring* = "NCSTRING"
NimNil* = "NIM_NIL"
CNil* = "NULL"
NimStrlitFlag* = "NIM_STRLIT_FLAG"
CVoid* = "void"
CPointer* = "void*"
CConstPointer* = "NIM_CONST void*"
proc cIntType*(bits: BiggestInt): Snippet =
"NI" & $bits
proc cUintType*(bits: BiggestInt): Snippet =
"NU" & $bits
type
IfBuilderState* = enum
WaitingIf, WaitingElseIf, InBlock
IfBuilder* = object
state*: IfBuilderState

View File

@@ -1,668 +0,0 @@
type VarKind = enum
Local
Global
Threadvar
Const
AlwaysConst ## const even on C++
proc addVarHeader(builder: var Builder, kind: VarKind) =
## adds modifiers for given var kind:
## Local has no modifier
## Global has `static` modifier
## Const has `static NIM_CONST` modifier
## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
## Threadvar is unimplemented
case kind
of Local: discard
of Global:
builder.add("static ")
of Const:
builder.add("static NIM_CONST ")
of AlwaysConst:
builder.add("static const ")
of Threadvar:
doAssert false, "unimplemented"
proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
## adds a variable declaration to the builder
builder.addVarHeader(kind)
builder.add(typ)
builder.add(" ")
builder.add(name)
if initializer.len != 0:
builder.add(" = ")
builder.add(initializer)
builder.addLineEnd(";")
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
## adds a variable declaration to the builder, with the `body` building the type
builder.addVarHeader(kind)
body
builder.add(" ")
builder.add(name)
builder.addLineEnd(";")
template addVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string,
typ: Snippet, initializerBody: typed) =
## adds a variable declaration to the builder, with
## `initializerBody` building the initializer. initializer must be provided
builder.addVarHeader(kind)
builder.add(typ)
builder.add(" ")
builder.add(name)
builder.add(" = ")
initializerBody
builder.addLineEnd(";")
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
typeBody, initializerBody: typed) =
## adds a variable declaration to the builder, with `typeBody` building the type, and
## `initializerBody` building the initializer. initializer must be provided
builder.addVarHeader(kind)
typeBody
builder.add(" ")
builder.add(name)
builder.add(" = ")
initializerBody
builder.addLineEnd(";")
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
## adds an array variable declaration to the builder
builder.addVarHeader(kind)
builder.add(elementType)
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addIntValue(len)
builder.add("]")
if initializer.len != 0:
builder.add(" = ")
builder.add(initializer)
builder.addLineEnd(";")
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
## adds an array variable declaration to the builder with the initializer built according to `body`
builder.addVarHeader(kind)
builder.add(elementType)
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addIntValue(len)
builder.add("] = ")
body
builder.addLineEnd(";")
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
## adds a typedef declaration to the builder with name `name` and type as
## built in `typeBody`
builder.add("typedef ")
typeBody
builder.add(" ")
builder.add(name)
builder.addLineEnd(";")
proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
builder.add("typedef ")
builder.add(CallingConvToStr[callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
builder.addLineEnd(";")
template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, typeBody: typed) =
## adds an array typedef declaration to the builder with name `name`,
## length `len`, and element type as built in `typeBody`
builder.add("typedef ")
typeBody
builder.add(" ")
builder.add(name)
builder.add("[")
builder.addIntValue(len)
builder.addLineEnd("];")
type
StructInitializerKind = enum
siOrderedStruct ## struct constructor, but without named fields on C
siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
siArray ## array constructor
siWrapper ## wrapper for a single field, generates it verbatim
StructInitializer = object
## context for building struct initializers, i.e. `{ field1, field2 }`
kind: StructInitializerKind
## if true, fields will not be named, instead values are placed in order
needsComma: bool
proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
## starts building a struct initializer, i.e. braced initializer list
result = StructInitializer(kind: kind, needsComma: false)
if kind != siWrapper:
builder.add("{")
template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
## adds a field to a struct initializer, with the value built in `valueBody`
if constr.needsComma:
assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
builder.add(", ")
else:
constr.needsComma = true
case constr.kind
of siArray, siWrapper:
# no name, can just add value
valueBody
of siOrderedStruct:
# positional init - name not used in output (empty allowed for anonymous unions)
valueBody
of siNamedStruct:
# designated init - empty name for anonymous unions (skips .name = prefix)
if name.len != 0:
builder.add(".")
builder.add(name)
builder.add(" = ")
valueBody
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
## finishes building a struct initializer
if constr.kind != siWrapper:
builder.add("}")
template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
## builds a struct initializer, i.e. `{ field1, field2 }`
## a `var StructInitializer` must be declared and passed as a parameter so
## that it can be used with `addField`
constr = builder.initStructInitializer(kind)
body
builder.finishStructInitializer(constr)
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## adds a field inside a struct/union type
obj.add('\t')
obj.add(typ)
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
## adds an array field inside a struct/union type
obj.add('\t')
obj.add(elementType)
obj.add(" ")
obj.add(name)
obj.add("[")
obj.addIntValue(len)
obj.add("]")
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
## adds an field inside a struct/union type, based on an `skField` symbol
obj.add('\t')
if field.alignment > 0:
obj.add("NIM_ALIGN(")
obj.addIntValue(field.alignment)
obj.add(") ")
obj.add(typ)
if sfNoalias in field.flags:
obj.add(" NIM_NOALIAS")
obj.add(" ")
obj.add(name)
if isFlexArray:
obj.add("[SEQ_DECL_SIZE]")
if field.bitsize != 0:
obj.add(":")
obj.addIntValue(field.bitsize)
if initializer.len != 0:
obj.add(initializer)
obj.add(";\n")
proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
obj.add(CallingConvToStr[callConv])
obj.add("_PTR(")
obj.add(rettype)
obj.add(", ")
obj.add(name)
obj.add(")")
obj.add(params)
obj.add(";\n")
type
BaseClassKind = enum
## denotes how and whether or not the base class/RTTI should be stored
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
StructBuilderInfo = object
## context for building `struct` types
baseKind: BaseClassKind
named: bool
preFieldsLen: int
proc structOrUnion(t: PType): Snippet =
let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: "union"
else: "struct"
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
obj.add("struct")
if result.named:
obj.add(" ")
obj.add(name)
if baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.buf.len
if result.baseKind == bcSupField:
obj.addField(name = "Sup", typ = baseType)
proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = CChar)
if info.named:
obj.add("};\n")
else:
obj.add("}")
template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
## builds a struct type not based on a Nim type with fields according to `body`,
## `name` can be empty to build as a type expression and not a statement
let info = startSimpleStruct(obj, m, name, baseType)
body
finishSimpleStruct(obj, m, info)
proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
if tfPacked in t.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add(structOrUnion(t))
obj.add(" __attribute__((__packed__))")
else:
obj.add("#pragma pack(push, 1)\n")
obj.add(structOrUnion(t))
else:
obj.add(structOrUnion(t))
if result.named:
obj.add(" ")
obj.add(name)
if t.kind == tyObject:
if t.baseClass == nil:
if lacksMTypeField(t):
result.baseKind = bcNone
elif optTinyRtti in m.config.globalOptions:
result.baseKind = bcNoneTinyRtti
else:
result.baseKind = bcNoneRtti
elif m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
elif baseType.len != 0:
if m.compileToCpp:
result.baseKind = bcCppInherit
else:
result.baseKind = bcSupField
if result.baseKind == bcCppInherit:
obj.add(" : public ")
obj.add(baseType)
obj.add(" ")
obj.add("{\n")
result.preFieldsLen = obj.buf.len
case result.baseKind
of bcNone:
# rest of the options add a field or don't need it due to inheritance,
# we need to add the dummy field for uncheckedarray ahead of time
# so that it remains trailing
if t.itemId notin m.g.graph.memberProcsPerType and
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
# only consists of flexible array field, add *initial* dummy field
obj.addField(name = "dummy", typ = CChar)
of bcCppInherit: discard
of bcNoneRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
of bcNoneTinyRtti:
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
of bcSupField:
obj.addField(name = "Sup", typ = baseType)
proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
if info.baseKind == bcNone and info.preFieldsLen == obj.buf.len and
t.itemId notin m.g.graph.memberProcsPerType:
# no fields were added, add dummy field
obj.addField(name = "dummy", typ = CChar)
if info.named:
obj.add("};\n")
else:
obj.add("}")
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
obj.add("#pragma pack(pop)\n")
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
## builds a struct type directly based on `typ` with fields according to `body`,
## `name` can be empty to build as a type expression and not a statement
let info = startStruct(obj, m, typ, name, baseType)
body
finishStruct(obj, m, typ, info)
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
## adds a field with a `struct { ... }` type, building the fields according to `body`
obj.add('\t')
if tfPacked in parentTyp.flags:
if hasAttribute in CC[m.config.cCompiler].props:
obj.add("struct __attribute__((__packed__)) {\n")
else:
obj.add("#pragma pack(push, 1)\nstruct {")
else:
obj.add("struct {\n")
body
obj.add("} ")
obj.add(fieldName)
obj.add(";\n")
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
obj.add("#pragma pack(pop)\n")
template addAnonUnion(obj: var Builder; body: typed) =
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
obj.add "union{\n"
body
obj.add("};\n")
template addUnionType(obj: var Builder; body: typed) =
## adds a union type i.e. `union { ... }` with fields according to `body`
obj.add "union{\n"
body
obj.add("}")
type DeclVisibility = enum
None
Extern
ExternC
ImportLib
ExportLib
ExportLibVar
Private
StaticProc
proc addVisibilityPrefix(builder: var Builder, visibility: DeclVisibility) =
# internal proc
case visibility
of None: discard
of Extern:
builder.add("extern ")
of ExternC:
builder.add("NIM_EXTERNC ")
of ImportLib:
builder.add("N_LIB_IMPORT ")
of ExportLib:
builder.add("N_LIB_EXPORT ")
of ExportLibVar:
builder.add("N_LIB_EXPORT_VAR ")
of Private:
builder.add("N_LIB_PRIVATE ")
of StaticProc:
builder.add("static ")
template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
## adds a declaration as in `declBody` with the given visibility
builder.addVisibilityPrefix(visibility)
declBody
type ProcParamBuilder = object
needsComma: bool
proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
result = ProcParamBuilder(needsComma: false)
builder.add("(")
proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
if params.needsComma:
builder.add(")")
else:
builder.add("void)")
template cgDeclFrmt*(s: PSym): string =
s.constraint.strVal
proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add(typ)
builder.add(" ")
builder.add(name)
proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
var modifiedTyp = typ
if sfNoalias in param.flags:
modifiedTyp.add(" NIM_NOALIAS")
if sfCodegenDecl notin param.flags:
builder.add(modifiedTyp)
builder.add(" ")
builder.add(param.loc.snippet)
else:
builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add(typ)
proc addProcTypedParam(builder: var Builder, paramBuilder: var ProcParamBuilder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
if paramBuilder.needsComma:
builder.add(", ")
else:
paramBuilder.needsComma = true
builder.add(CallingConvToStr[callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
# does not exist in NIFC, needs to be proc pragma
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add("...")
template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
params = initProcParamBuilder(builder)
body
finishProcParamBuilder(builder, params)
type SimpleProcParam = tuple
name, typ: string
proc cProcParams(params: varargs[SimpleProcParam]): Snippet =
if params.len == 0: return "(void)"
result = "("
for i in 0 ..< params.len:
if i != 0: result.add(", ")
result.add(params[i].typ)
if params[i].name.len != 0:
result.add(" ")
result.add(params[i].name)
result.add(")")
template addProcHeaderWithParams(builder: var Builder, callConv: TCallingConvention,
name: string, rettype: Snippet, paramBuilder: typed) =
# on nifc should build something like (proc name params type pragmas
# with no body given
# or enforce this with secondary builder object
builder.add(CallingConvToStr[callConv])
builder.add("(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
paramBuilder
proc addProcHeader(builder: var Builder, callConv: TCallingConvention,
name: string, rettype, params: Snippet) =
# on nifc should build something like (proc name params type pragmas
# with no body given
# or enforce this with secondary builder object
addProcHeaderWithParams(builder, callConv, name, rettype):
builder.add(params)
proc addProcHeader(builder: var Builder, name: string, rettype, params: Snippet, isConstructor = false) =
# no callconv
builder.add(rettype)
builder.add(" ")
if isConstructor:
builder.add("__attribute__((constructor)) ")
builder.add(name)
builder.add(params)
proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
# on nifc should build something like (proc name params type pragmas
# with no body given
# or enforce this with secondary builder object
let noreturn = isNoReturn(m, prc)
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(naked) ")
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(noreturn) ")
builder.add(CallingConvToStr[prc.typ.callConv])
builder.add("(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
if addAttributes:
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((naked))")
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((noreturn))")
proc finishProcHeaderAsProto(builder: var Builder) =
builder.addLineEnd(";")
template finishProcHeaderWithBody(builder: var Builder, body: typed) =
builder.addLineEndIndent(" {")
body
builder.addLineEndDedent("}")
builder.addNewline
proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
isStatic = false, ignoreAttributes = false) =
# on nifc, builds full variable
if isStatic:
builder.add("static ")
let noreturn = isNoReturn(m, prc)
if not ignoreAttributes:
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(naked) ")
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(noreturn) ")
builder.add(CallingConvToStr[prc.typ.callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
if not ignoreAttributes:
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((naked))")
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((noreturn))")
# ensure we are just adding a variable:
builder.addLineEnd(";")
proc addProcVar(builder: var Builder, callConv: TCallingConvention,
name: string, params, rettype: Snippet,
isStatic = false, isVolatile = false) =
# on nifc, builds full variable
if isStatic:
builder.add("static ")
builder.add(CallingConvToStr[callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
if isVolatile:
builder.add("volatile ")
builder.add(name)
builder.add(")")
builder.add(params)
# ensure we are just adding a variable:
builder.addLineEnd(";")
proc addProcVar(builder: var Builder,
name: string, params, rettype: Snippet,
isStatic = false, isVolatile = false) =
# no callconv
if isStatic:
builder.add("static ")
builder.add(rettype)
builder.add(" (*")
if isVolatile:
builder.add("volatile ")
builder.add(name)
builder.add(")")
builder.add(params)
# ensure we are just adding a variable:
builder.addLineEnd(";")
type VarInitializerKind = enum
Assignment, CppConstructor
proc addVar(builder: var Builder, m: BModule, s: PSym, name: string, typ: Snippet, kind = Local, visibility: DeclVisibility = None, initializer: Snippet = "", initializerKind: VarInitializerKind = Assignment) =
if sfCodegenDecl in s.flags:
builder.add(runtimeFormat(s.cgDeclFrmt, [typ, name]))
if initializer.len != 0:
if initializerKind == Assignment:
builder.add(" = ")
builder.add(initializer)
builder.addLineEnd(";")
return
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
builder.add("NIM_ALIGN(" & $s.alignment & ") ")
builder.addVisibilityPrefix(visibility)
if kind == Threadvar:
if optThreads in m.config.globalOptions:
let sym = s.typ.sym
if sym != nil and sfCppNonPod in sym.flags:
builder.add("NIM_THREAD_LOCAL ")
else: builder.add("NIM_THREADVAR ")
else:
builder.addVarHeader(kind)
builder.add(typ)
if sfRegister in s.flags: builder.add(" register")
if sfVolatile in s.flags: builder.add(" volatile")
if sfNoalias in s.flags: builder.add(" NIM_NOALIAS")
builder.add(" ")
builder.add(name)
if initializer.len != 0:
if initializerKind == Assignment:
builder.add(" = ")
builder.add(initializer)
builder.addLineEnd(";")
proc addInclude(builder: var Builder, value: Snippet) =
builder.addLineEnd("#include " & value)

View File

@@ -1,259 +0,0 @@
proc constType(t: Snippet): Snippet =
# needs manipulation of `t` in nifc
"NIM_CONST " & t
proc constPtrType(t: Snippet): Snippet =
t & "* NIM_CONST"
proc ptrConstType(t: Snippet): Snippet =
"NIM_CONST " & t & "*"
proc ptrType(t: Snippet): Snippet =
t & "*"
proc cppRefType(t: Snippet): Snippet =
t & "&"
const
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
"N_STDCALL", "N_CDECL", "N_SAFECALL",
"N_SYSCALL", # this is probably not correct for all platforms,
# but one can #define it to what one wants
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
"N_NOCONV" #ccMember is N_NOCONV
]
proc procPtrTypeUnnamed(rettype, params: Snippet): Snippet =
rettype & "(*)" & params
proc procPtrTypeUnnamedNimCall(rettype, params: Snippet): Snippet =
rettype & "(N_RAW_NIMCALL*)" & params
proc procPtrTypeUnnamed(callConv: TCallingConvention, rettype, params: Snippet): Snippet =
CallingConvToStr[callConv] & "_PTR(" & rettype & ", )" & params
type CppCaptureKind = enum None, ByReference, ByCopy
template addCppLambda(builder: var Builder, captures: CppCaptureKind, params: Snippet, body: typed) =
builder.add("[")
case captures
of None: discard
of ByReference: builder.add("&")
of ByCopy: builder.add("=")
builder.add("] ")
builder.add(params)
builder.addLineEndIndent(" {")
body
builder.addLineEndDedent("}")
proc cCast(typ, value: Snippet): Snippet =
"((" & typ & ") " & value & ")"
proc wrapPar(value: Snippet): Snippet =
# used for expression group, no-op on sexp
"(" & value & ")"
proc removeSinglePar(value: Snippet): Snippet =
# removes a single paren layer expected to exist, to silence Wparentheses-equality
assert value[0] == '(' and value[^1] == ')'
value[1..^2]
template addCast(builder: var Builder, typ: Snippet, valueBody: typed) =
## adds a cast to `typ` with value built by `valueBody`
builder.add "(("
builder.add typ
builder.add ") "
valueBody
builder.add ")"
proc cAddr(value: Snippet): Snippet =
"(&" & value & ")"
proc cLabelAddr(value: TLabel): Snippet =
"&&" & value
proc cDeref(value: Snippet): Snippet =
"(*" & value & ")"
proc subscript(a, b: Snippet): Snippet =
a & "[" & b & "]"
proc dotField(a, b: Snippet): Snippet =
a & "." & b
proc derefField(a, b: Snippet): Snippet =
a & "->" & b
type CallBuilder = object
needsComma: bool
proc initCallBuilder(builder: var Builder, callee: Snippet): CallBuilder =
result = CallBuilder(needsComma: false)
builder.add(callee)
builder.add("(")
const cArgumentSeparator = ", "
proc addArgumentSeparator(builder: var Builder) =
# no-op on NIFC
# used by "single argument" builders
builder.add(cArgumentSeparator)
template addArgument(builder: var Builder, call: var CallBuilder, valueBody: typed) =
if call.needsComma:
builder.addArgumentSeparator()
else:
call.needsComma = true
valueBody
proc finishCallBuilder(builder: var Builder, call: CallBuilder) =
builder.add(")")
template addCall(builder: var Builder, call: out CallBuilder, callee: Snippet, body: typed) =
call = initCallBuilder(builder, callee)
body
finishCallBuilder(builder, call)
proc addCall(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
builder.add(callee)
builder.add("(")
if args.len != 0:
builder.add(args[0])
for i in 1 ..< args.len:
builder.add(", ")
builder.add(args[i])
builder.add(")")
proc cCall(callee: Snippet, args: varargs[Snippet]): Snippet =
result = callee
result.add("(")
if args.len != 0:
result.add(args[0])
for i in 1 ..< args.len:
result.add(", ")
result.add(args[i])
result.add(")")
proc addSizeof(builder: var Builder, val: Snippet) =
builder.add("sizeof(")
builder.add(val)
builder.add(")")
proc addAlignof(builder: var Builder, val: Snippet) =
builder.add("NIM_ALIGNOF(")
builder.add(val)
builder.add(")")
proc addOffsetof(builder: var Builder, val, member: Snippet) =
builder.add("offsetof(")
builder.add(val)
builder.add(", ")
builder.add(member)
builder.add(")")
template cSizeof(val: Snippet): Snippet =
"sizeof(" & val & ")"
template cAlignof(val: Snippet): Snippet =
"NIM_ALIGNOF(" & val & ")"
template cOffsetof(val, member: Snippet): Snippet =
"offsetof(" & val & ", " & member & ")"
type TypedBinaryOp = enum
Add, Sub, Mul, Div, Mod
Shr, Shl, BitAnd, BitOr, BitXor
const typedBinaryOperators: array[TypedBinaryOp, string] = [
Add: "+",
Sub: "-",
Mul: "*",
Div: "/",
Mod: "%",
Shr: ">>",
Shl: "<<",
BitAnd: "&",
BitOr: "|",
BitXor: "^"
]
type TypedUnaryOp = enum
Neg, BitNot
const typedUnaryOperators: array[TypedUnaryOp, string] = [
Neg: "-",
BitNot: "~",
]
type UntypedBinaryOp = enum
LessEqual, LessThan, GreaterEqual, GreaterThan, Equal, NotEqual
And, Or
const untypedBinaryOperators: array[UntypedBinaryOp, string] = [
LessEqual: "<=",
LessThan: "<",
GreaterEqual: ">=",
GreaterThan: ">",
Equal: "==",
NotEqual: "!=",
And: "&&",
Or: "||"
]
type UntypedUnaryOp = enum
Not
const untypedUnaryOperators: array[UntypedUnaryOp, string] = [
Not: "!"
]
proc addOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
builder.add('(')
builder.add(a)
builder.add(' ')
builder.add(typedBinaryOperators[binOp])
builder.add(' ')
builder.add(b)
builder.add(')')
proc addOp(builder: var Builder, unOp: TypedUnaryOp, t: Snippet, a: Snippet) =
builder.add('(')
builder.add(typedUnaryOperators[unOp])
builder.add('(')
builder.add(a)
builder.add("))")
proc addOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
builder.add('(')
builder.add(a)
builder.add(' ')
builder.add(untypedBinaryOperators[binOp])
builder.add(' ')
builder.add(b)
builder.add(')')
proc addOp(builder: var Builder, unOp: UntypedUnaryOp, a: Snippet) =
builder.add('(')
builder.add(untypedUnaryOperators[unOp])
builder.add('(')
builder.add(a)
builder.add("))")
template cOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
'(' & a & ' ' & typedBinaryOperators[binOp] & ' ' & b & ')'
template cOp(binOp: TypedUnaryOp, t: Snippet, a: Snippet): Snippet =
'(' & typedUnaryOperators[binOp] & '(' & a & "))"
template cOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
'(' & a & ' ' & untypedBinaryOperators[binOp] & ' ' & b & ')'
template cOp(binOp: UntypedUnaryOp, a: Snippet): Snippet =
'(' & untypedUnaryOperators[binOp] & '(' & a & "))"
template cIfExpr(cond, a, b: Snippet): Snippet =
# XXX used for `min` and `max`, maybe add nifc primitives for these
"(" & cond & " ? " & a & " : " & b & ")"
template cUnlikely(val: Snippet): Snippet =
"NIM_UNLIKELY(" & val & ")"

View File

@@ -1,329 +0,0 @@
template addAssignmentWithValue(builder: var Builder, lhs: Snippet, valueBody: typed) =
builder.add(lhs)
builder.add(" = ")
valueBody
builder.addLineEnd(";")
template addFieldAssignmentWithValue(builder: var Builder, lhs: Snippet, name: string, valueBody: typed) =
builder.add(lhs)
builder.add("." & name & " = ")
valueBody
builder.addLineEnd(";")
template addAssignment(builder: var Builder, lhs, rhs: Snippet) =
builder.addAssignmentWithValue(lhs):
builder.add(rhs)
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
builder.addFieldAssignmentWithValue(lhs, name):
builder.add(rhs)
template addMutualFieldAssignment(builder: var Builder, lhs, rhs: Snippet, name: string) =
builder.addFieldAssignmentWithValue(lhs, name):
builder.add(rhs)
builder.add("." & name)
template addAssignment(builder: var Builder, lhs: Snippet, rhs: int | int64 | uint64 | Int128) =
builder.addAssignmentWithValue(lhs):
builder.addIntValue(rhs)
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: int | int64 | uint64 | Int128) =
builder.addFieldAssignmentWithValue(lhs, name):
builder.addIntValue(rhs)
template addDerefFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
builder.add(lhs)
builder.add("->" & name & " = ")
builder.add(rhs)
builder.addLineEnd(";")
template addSubscriptAssignment(builder: var Builder, lhs: Snippet, index: Snippet, rhs: Snippet) =
builder.add(lhs)
builder.add("[" & index & "] = ")
builder.add(rhs)
builder.addLineEnd(";")
template addStmt(builder: var Builder, stmtBody: typed) =
## makes an expression built by `stmtBody` into a statement
stmtBody
builder.addLineEnd(";")
proc addCallStmt(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
builder.addStmt():
builder.addCall(callee, args)
# XXX blocks need indent tracker in `Builder` object
template addSingleIfStmt(builder: var Builder, cond: Snippet, body: typed) =
builder.add("if (")
builder.add(cond)
builder.addLineEndIndent(") {")
body
builder.addLineEndDedent("}")
template addSingleIfStmtWithCond(builder: var Builder, condBody: typed, body: typed) =
builder.add("if (")
condBody
builder.addLineEndIndent(") {")
body
builder.addLineEndDedent("}")
proc initIfStmt(builder: var Builder): IfBuilder =
IfBuilder(state: WaitingIf)
proc finishIfStmt(builder: var Builder, stmt: IfBuilder) =
assert stmt.state != InBlock
builder.addNewline()
template addIfStmt(builder: var Builder, stmt: out IfBuilder, body: typed) =
stmt = initIfStmt(builder)
body
finishIfStmt(builder, stmt)
proc initElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet) =
case stmt.state
of WaitingIf:
builder.add("if (")
of WaitingElseIf:
builder.add(" else if (")
else: assert false, $stmt.state
builder.add(cond)
builder.addLineEndIndent(") {")
stmt.state = InBlock
proc initElseBranch(builder: var Builder, stmt: var IfBuilder) =
assert stmt.state == WaitingElseIf, $stmt.state
builder.addLineEndIndent(" else {")
stmt.state = InBlock
proc finishBranch(builder: var Builder, stmt: var IfBuilder) =
builder.addDedent("}")
stmt.state = WaitingElseIf
template addElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet, body: typed) =
initElifBranch(builder, stmt, cond)
body
finishBranch(builder, stmt)
template addElseBranch(builder: var Builder, stmt: var IfBuilder, body: typed) =
initElseBranch(builder, stmt)
body
finishBranch(builder, stmt)
proc initForRange(builder: var Builder, i, start, bound: Snippet, inclusive: bool = false) =
builder.add("for (")
builder.add(i)
builder.add(" = ")
builder.add(start)
builder.add("; ")
builder.add(i)
if inclusive:
builder.add(" <= ")
else:
builder.add(" < ")
builder.add(bound)
builder.add("; ")
builder.add(i)
builder.addLineEndIndent("++) {")
proc initForStep(builder: var Builder, i, start, bound, step: Snippet, inclusive: bool = false) =
builder.add("for (")
builder.add(i)
builder.add(" = ")
builder.add(start)
builder.add("; ")
builder.add(i)
if inclusive:
builder.add(" <= ")
else:
builder.add(" < ")
builder.add(bound)
builder.add("; ")
builder.add(i)
builder.add(" += ")
builder.add(step)
builder.addLineEndIndent(") {")
proc finishFor(builder: var Builder) {.inline.} =
builder.addLineEndDedent("}")
template addForRangeExclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
initForRange(builder, i, start, bound, false)
body
finishFor(builder)
template addForRangeInclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
initForRange(builder, i, start, bound, true)
body
finishFor(builder)
template addSwitchStmt(builder: var Builder, val: Snippet, body: typed) =
builder.add("switch (")
builder.add(val)
builder.addLineEnd(") {") # no indent
body
builder.addLineEnd("}")
template addSingleSwitchCase(builder: var Builder, val: Snippet, body: typed) =
builder.add("case ")
builder.add(val)
builder.addLineEndIndent(":")
body
builder.addLineEndDedent("")
type
SwitchCaseState = enum
None, Of, Else, Finished
SwitchCaseBuilder = object
state: SwitchCaseState
proc addCase(builder: var Builder, info: var SwitchCaseBuilder, val: Snippet) =
if info.state != Of:
assert info.state == None
info.state = Of
builder.add("case ")
builder.add(val)
builder.addLineEndIndent(":")
proc addCaseRange(builder: var Builder, info: var SwitchCaseBuilder, first, last: Snippet) =
if info.state != Of:
assert info.state == None
info.state = Of
builder.add("case ")
builder.add(first)
builder.add(" ... ")
builder.add(last)
builder.addLineEndIndent(":")
proc addCaseElse(builder: var Builder, info: var SwitchCaseBuilder) =
assert info.state == None
info.state = Else
builder.addLineEndIndent("default:")
template addSwitchCase(builder: var Builder, info: out SwitchCaseBuilder, caseBody, body: typed) =
info = SwitchCaseBuilder(state: None)
caseBody
info.state = Finished
body
builder.addLineEndDedent("")
template addSwitchElse(builder: var Builder, body: typed) =
builder.addLineEndIndent("default:")
body
builder.addLineEndDedent("")
proc addBreak(builder: var Builder) =
builder.addLineEnd("break;")
type ScopeBuilder = object
inside: bool
proc initScope(builder: var Builder): ScopeBuilder =
builder.addLineEndIndent("{")
result = ScopeBuilder(inside: true)
proc finishScope(builder: var Builder, scope: var ScopeBuilder) =
assert scope.inside, "scope not inited"
builder.addLineEndDedent("}")
scope.inside = false
template addScope(builder: var Builder, body: typed) =
builder.addLineEndIndent("{")
body
builder.addLineEndDedent("}")
type WhileBuilder = object
inside: bool
proc initWhileStmt(builder: var Builder, cond: Snippet): WhileBuilder =
builder.add("while (")
builder.add(cond)
builder.addLineEndIndent(") {")
result = WhileBuilder(inside: true)
proc finishWhileStmt(builder: var Builder, stmt: var WhileBuilder) =
assert stmt.inside, "while stmt not inited"
builder.addLineEndDedent("}")
stmt.inside = false
template addWhileStmt(builder: var Builder, cond: Snippet, body: typed) =
builder.add("while (")
builder.add(cond)
builder.addLineEndIndent(") {")
body
builder.addLineEndDedent("}")
proc addLabel(builder: var Builder, name: TLabel) =
builder.add(name)
builder.addLineEnd(": ;")
proc addReturn(builder: var Builder) =
builder.addLineEnd("return;")
proc addReturn(builder: var Builder, value: Snippet) =
builder.add("return ")
builder.add(value)
builder.addLineEnd(";")
proc addGoto(builder: var Builder, label: TLabel) =
builder.add("goto ")
builder.add(label)
builder.addLineEnd(";")
proc addComputedGoto(builder: var Builder, value: Snippet) =
builder.add("goto *")
builder.add(value)
builder.addLineEnd(";")
proc addIncr(builder: var Builder, val: Snippet) =
builder.add(val)
builder.addLineEnd("++;")
proc addDecr(builder: var Builder, val: Snippet) =
builder.add(val)
builder.addLineEnd("--;")
proc addInPlaceOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
builder.add(a)
builder.add(' ')
builder.add(typedBinaryOperators[binOp])
builder.add("= ")
builder.add(b)
builder.addLineEnd(";")
proc addInPlaceOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
builder.add(a)
builder.add(' ')
builder.add(untypedBinaryOperators[binOp])
builder.add("= ")
builder.add(b)
builder.addLineEnd(";")
proc cInPlaceOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
result = ""
result.add(a)
result.add(' ')
result.add(typedBinaryOperators[binOp])
result.add("= ")
result.add(b)
result.add(";\n")
proc cInPlaceOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
result = ""
result.add(a)
result.add(' ')
result.add(untypedBinaryOperators[binOp])
result.add("= ")
result.add(b)
result.add(";\n")
template addCPragma(builder: var Builder, val: Snippet) =
builder.addNewline()
builder.add("#pragma ")
builder.add(val)
builder.addNewline()
proc addDiscard(builder: var Builder, val: Snippet) =
builder.add("(void)")
builder.add(val)
builder.addLineEnd(";")

View File

@@ -24,7 +24,6 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
result = false
var n = le
while true:
# do NOT follow nkHiddenDeref here!
@@ -47,7 +46,6 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
# cannot analyse the location; assume the worst
return true
result = false
if le != nil:
for i in 1..<ri.len:
let r = ri[i]
@@ -76,188 +74,138 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
else:
result = false
proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
if returnType.kind in {tyVar, tyLent}:
# we don't need to worry about var/lent return types
result = false
elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil:
let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
var op = initLocExpr(p, newSymNode(dtor))
var callee = rdLoc(op)
let destroyArg =
if dtor.typ.firstParamType.kind == tyVar:
cAddr(rdLoc(tmp))
else:
rdLoc(tmp)
let destroy = cCall(callee, destroyArg)
raiseExitCleanup(p, destroy)
result = true
else:
result = false
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
result: var Builder, call: var CallBuilder) =
callee, params: Rope) =
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
genLineDir(p, ri)
var pl = callee & "(" & params
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ.returnType != nil:
var flags: TAssignmentFlags = {}
if typ.returnType.kind in {tyOpenArray, tyVarargs}:
# perhaps generate no temp if the call doesn't have side effects
flags.incl needTempForOpenArray
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if params.len != 0: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
let rad = addrLoc(p.config, d)
result.addArgument(call):
result.add(rad)
result.finishCallBuilder(call)
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(result))
pl.add(addrLoc(p.config, d))
pl.add(");\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
let ratmp = addrLoc(p.config, tmp)
result.addArgument(call):
result.add(ratmp)
result.finishCallBuilder(call)
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(result))
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add(");\n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
result.finishCallBuilder(call)
pl.add(")")
if p.module.compileToCpp:
if lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.snippet = extract(result)
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
d = getTempCpp(p, typ.returnType, extract(result))
if d.k == locNone and p.splitDecls == 0:
getTempCpp(p, typ[0], d, pl)
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = extract(result)
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
if d.k == locNone: getTemp(p, typ[0], d)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
elif isHarmlessStore(p, canRaise, d):
var useTemp = false
if d.k == locNone:
useTemp = true
d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = extract(result)
genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not (useTemp and cleanupTemp(p, typ.returnType, d)):
raiseExit(p)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var list = initLoc(locCall, d.lode, OnUnknown)
list.snippet = extract(result)
genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
if canRaise:
if not cleanupTemp(p, typ.returnType, tmp):
raiseExit(p)
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, tmp, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
else:
finishCallBuilder(result, call)
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(result))
pl.add(");\n")
line(p, cpsStmts, pl)
if canRaise: raiseExit(p)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
proc reifiedOpenArray(n: PNode): bool {.inline.} =
var x = n
while true:
case x.kind
of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
x = x[0]
of nkHiddenStdConv:
x = x[1]
else:
break
while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
x = x[0]
if x.kind == nkSym and x.sym.kind == skParam:
result = false
else:
result = true
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
var a = initLocExpr(p, q[1])
var b = initLocExpr(p, q[2])
var c = initLocExpr(p, q[3])
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
# are mapped into ctPtrToArray, the dereference of which is skipped
# in the `genDeref`. We need to skip these ptrs here
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) =
var a, b, c: TLoc
initLocExpr(p, q[1], a)
initLocExpr(p, q[2], b)
initLocExpr(p, q[3], c)
# but first produce the required index checks:
if optBoundsCheck in p.options:
genBoundsCheck(p, a, b, c, ty)
if prepareForMutation:
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
genBoundsCheck(p, a, b, c)
let ty = skipTypes(a.t, abstractVar+{tyPtr})
let dest = getTypeDesc(p.module, destType)
let ra = rdLoc(a)
let rb = rdLoc(b)
let rc = rdLoc(c)
let lengthExpr = cOp(Add, NimInt, cOp(Sub, NimInt, rc, rb), cIntValue(1))
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
case ty.kind
of tyArray:
let first = toInt64(firstOrd(p.config, ty))
if first == 0:
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
else:
let lit = cIntLiteral(first)
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, cOp(Sub, NimInt, rb, lit))), lengthExpr)
var lit = newRopeAppender()
intLiteral(first, lit)
result = ("($4*)($1)+(($2)-($3))" %
[rdLoc(a), rdLoc(b), lit, dest],
lengthExpr)
of tyOpenArray, tyVarargs:
let data = if reifiedOpenArray(q[1]): dotField(ra, "Field0") else: ra
result = (cCast(ptrType(dest), cOp(Add, NimInt, data, rb)), lengthExpr)
if reifiedOpenArray(q[1]):
result = ("($3*)($1.Field0)+($2)" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
else:
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
of tyUncheckedArray, tyCstring:
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
lengthExpr)
of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.usesSso():
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
if p.config.usesSso() and
skipTypes(a.t, abstractVar + abstractInst).kind == tyString:
let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra
else: addrLoc(p.config, a)
result = (
cCast(ptrType(dest), cOp(Add, NimInt,
cCall(cgsymValue(p.module, "nimStrData"), strPtr), rb)),
lengthExpr)
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if atyp.kind in {tyVar} and not compileToCpp(p.module):
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
lengthExpr)
else:
var val: Snippet
if atyp.kind in {tyVar} and not compileToCpp(p.module):
val = cDeref(ra)
else:
val = ra
result = (
cIfExpr(dataFieldAccessor(p, val),
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
NimNil),
lengthExpr)
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
lengthExpr)
else:
result = ("", "")
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
var q = skipConv(n)
var skipped = false
while q.kind == nkStmtListExpr and q.len > 0:
@@ -271,83 +219,46 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
for i in 0..<q.len-1:
genStmts(p, q[i])
q = q.lastSon
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
result.add(x)
result.addArgumentSeparator()
result.add(y)
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
result.add x & ", " & y
else:
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
var a: TLoc
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
case skipTypes(a.t, abstractVar+{tyStatic}).kind
of tyOpenArray, tyVarargs:
let ra = rdLoc(a)
if reifiedOpenArray(n):
if a.t.kind in {tyVar, tyLent}:
result.add(derefField(ra, "Field0"))
result.addArgumentSeparator()
result.add(derefField(ra, "Field1"))
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
else:
result.add(dotField(ra, "Field0"))
result.addArgumentSeparator()
result.add(dotField(ra, "Field1"))
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
else:
result.add(ra)
result.addArgumentSeparator()
result.add(ra & "Len_0")
result.add "$1, $1Len_0" % [rdLoc(a)]
of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.usesSso():
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
if p.config.usesSso() and
skipTypes(n.typ, abstractVar + abstractInst).kind == tyString:
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), cDeref(ra)))
else:
result.add(cCall(cgsymValue(p.module, "nimStrData"), addrLoc(p.config, a)))
result.addArgumentSeparator()
result.add(lenExpr(p, a))
elif ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t)
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
result.addArgumentSeparator()
result.add(lt)
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
else:
let ra = a.rdLoc
let la = lenExpr(p, a)
result.add(cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
result.addArgumentSeparator()
result.add(la)
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
of tyArray:
let ra = rdLoc(a)
result.add(ra)
result.addArgumentSeparator()
result.addIntValue(lengthOrd(p.config, a.t))
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
of tyPtr, tyRef:
case elementType(a.t).kind
case lastSon(a.t).kind
of tyString, tySequence:
let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t)
if p.config.usesSso():
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet))
else:
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
result.addArgumentSeparator()
result.add(lt)
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
[a.rdLoc, lenExpr(p, t), dataField(p),
dataFieldAccessor(p, "*" & a.rdLoc)]
of tyArray:
let ra = rdLoc(a)
result.add(ra)
result.addArgumentSeparator()
result.addIntValue(lengthOrd(p.config, elementType(a.t)))
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -356,80 +267,63 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
# Bug https://github.com/status-im/nimbus-eth2/issues/1549
# Aliasing is preferred over stack overflows.
# Also don't regress for non ARC-builds, too risky.
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc} and
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and
getSize(p.config, a.lode.typ) < 1024:
result = getTemp(p, a.lode.typ, needsInit=false)
getTemp(p, a.lode.typ, result, needsInit=false)
genAssignment(p, result, a, {})
else:
result = a
proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
result = getTemp(p, a.lode.typ, needsInit=false)
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
getTemp(p, a.lode.typ, result, needsInit=false)
genAssignment(p, result, a, {})
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
var a = initLocExpr(p, n[0])
let tmp = withTmpIfNeeded(p, a, needsTmp)
let ra = if p.config.usesSso(): byRefLoc(p, tmp) else: tmp.rdLoc
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
var a: TLoc
initLocExpr(p, n[0], a)
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
var a: TLoc
if n.kind == nkStringToCString:
genArgStringToCString(p, n, result, needsTmp)
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
var n = if n.kind != nkHiddenAddr: n else: n[0]
openArrayLoc(p, param.typ, n, result)
elif ccgIntroducedPtr(p.config, param, call[0].typ.returnType) and
(optByRef notin param.options or not p.module.compileToCpp):
a = initLocExpr(p, n)
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
initLocExpr(p, n, a)
if n.kind in {nkCharLit..nkNilLit}:
addAddrLoc(p.config, expressionsNeedsTmp(p, a), result)
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
else:
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
n.kind == nkHiddenAddr:
# bug #23748: we need to introduce a temporary here. The expression type
# will be a reference in C++ and we cannot create a temporary reference
# variable. Thus, we create a temporary pointer variable instead.
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
if needsIndirect:
n.typ = n.typ.exactReplica(p.module.idgen)
n.typ.incl tfVarIsPtr
a = initLocExprSingleUse(p, n)
a = withTmpIfNeeded(p, a, needsTmp)
if needsIndirect: a.flags.incl lfIndirect
initLocExprSingleUse(p, n[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
# means '*T'. See posix.nim for lots of examples that do that in the wild.
let callee = call[0]
if callee.kind == nkSym and
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
needsIndirect:
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
addAddrLoc(p.config, a, result)
else:
addRdLoc(a, result)
else:
a = initLocExprSingleUse(p, n)
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
let typ = skipTypes(param.typ, abstractPtrs)
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
a.snippet = cCast(getTypeDesc(p.module, param.typ), rdCharLoc(a))
initLocExprSingleUse(p, n, a)
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
#assert result != nil
proc genArgNoParam(p: BProc, n: PNode; result: var Builder; needsTmp = false) =
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
var a: TLoc
if n.kind == nkStringToCString:
genArgStringToCString(p, n, result, needsTmp)
else:
a = initLocExprSingleUse(p, n)
initLocExprSingleUse(p, n, a)
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
import aliasanalysis
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
result = false
for p in potentialWrites:
if p.aliases(n) != no or n.aliases(p) != no:
return true
@@ -444,6 +338,35 @@ proc skipTrivialIndirections(n: PNode): PNode =
result = result[1]
else: break
proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
case n.kind:
of nkLiterals, nkIdent, nkFormalParams: discard
of nkSym:
if mutate: result.add n
of nkAsgn, nkFastAsgn, nkSinkAsgn:
getPotentialWrites(n[0], true, result)
getPotentialWrites(n[1], mutate, result)
of nkAddr, nkHiddenAddr:
getPotentialWrites(n[0], true, result)
of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr:
getPotentialWrites(n[0], mutate, result)
of nkCallKinds:
case n.getMagic:
of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset:
getPotentialWrites(n[1], true, result)
for i in 2..<n.len:
getPotentialWrites(n[i], mutate, result)
of mSwap:
for i in 1..<n.len:
getPotentialWrites(n[i], true, result)
else:
for i in 1..<n.len:
getPotentialWrites(n[i], mutate, result)
else:
for s in n:
getPotentialWrites(s, mutate, result)
proc getPotentialReads(n: PNode; result: var seq[PNode]) =
case n.kind:
of nkLiterals, nkIdent, nkFormalParams: discard
@@ -452,44 +375,41 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
for s in n:
getPotentialReads(s, result)
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
# We must generate temporaries in cases like #14396
# to keep the strict Left-To-Right evaluation
var needTmp = newSeq[bool](ri.len - 1)
var potentialWrites: seq[PNode] = @[]
var potentialWrites: seq[PNode]
for i in countdown(ri.len - 1, 1):
if ri[i].skipTrivialIndirections.kind == nkSym:
needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
else:
#if not ri[i].typ.isCompileTimeOnly:
var potentialReads: seq[PNode] = @[]
var potentialReads: seq[PNode]
getPotentialReads(ri[i], potentialReads)
for n in potentialReads:
if not needTmp[i - 1]:
needTmp[i - 1] = potentialAlias(n, potentialWrites)
getPotentialWrites(ri[i], false, potentialWrites)
when false:
# this optimization is wrong, see bug #23748
if ri[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
if ri[i].kind in {nkHiddenAddr, nkAddr}:
# Optimization: don't use a temp, if we would only take the address anyway
needTmp[i - 1] = false
var oldLen = result.len
for i in 1..<ri.len:
if i < typ.n.len:
if i < typ.len:
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
if not paramType.typ.isCompileTimeOnly:
var arg = newBuilder("")
genArg(p, ri[i], paramType.sym, ri, arg, needTmp[i-1])
if arg.buf.len != 0:
result.addArgument(argBuilder):
result.add(extract(arg))
if oldLen != result.len:
result.add(", ")
oldLen = result.len
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
else:
var arg = newBuilder("")
genArgNoParam(p, ri[i], arg, needTmp[i-1])
if arg.buf.len != 0:
result.addArgument(argBuilder):
result.add(extract(arg))
if oldLen != result.len:
result.add(", ")
oldLen = result.len
genArgNoParam(p, ri[i], result, needTmp[i-1])
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
@@ -497,110 +417,90 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
res = res & "_actual".rope
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op: TLoc
# this is a hotspot in the compiler
var op = initLocExpr(p, ri[0])
initLocExpr(p, ri[0], op)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInstOwned)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var params = newRopeAppender()
genParams(p, ri, typ, params)
var callee = rdLoc(op)
if p.hcrOn and ri[0].kind == nkSym:
callee.addActualSuffixForHCR(p.module.module, ri[0].sym)
var res = newBuilder("")
var call = initCallBuilder(res, callee)
genParams(p, ri, typ, res, call)
fixupCall(p, le, ri, d, res, call)
fixupCall(p, le, ri, d, callee, params)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
template callProc(rp, params, pTyp: Snippet): Snippet =
let e = dotField(rp, "ClE_0")
let p = dotField(rp, "ClP_0")
let eCall =
# note `params` here is actually multiple params
if params.len == 0:
cCall(p, e)
else:
cCall(p, params, e)
cIfExpr(e,
eCall,
cCall(cCast(pTyp, p), params))
proc addComma(r: Rope): Rope =
if r.len == 0: r else: r & ", "
template callIter(rp, params: Snippet): Snippet =
# we know the env exists
let e = dotField(rp, "ClE_0")
let p = dotField(rp, "ClP_0")
# note `params` here is actually multiple params
if params.len == 0:
cCall(p, e)
else:
cCall(p, params, e)
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
var op = initLocExpr(p, ri[0])
var op: TLoc
initLocExpr(p, ri[0], op)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInstOwned)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
var params = newBuilder("")
var argBuilder = default(CallBuilder) # not initCallBuilder, we just want the params
genParams(p, ri, typ, params, argBuilder)
var pl = newRopeAppender()
genParams(p, ri, typ, pl)
template genCallPattern {.dirty.} =
let rp = rdLoc(op)
let pars = extract(params)
p.s(cpsStmts).addStmt():
if tfIterator in typ.flags:
p.s(cpsStmts).add(callIter(rp, pars))
else:
p.s(cpsStmts).add(callProc(rp, pars, rawProc))
if tfIterator in typ.flags:
lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
else:
lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
let rawProc = getClosureType(p.module, typ, clHalf)
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
if typ.returnType != nil:
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
# Great, we can use 'd':
if d.k == locNone:
d = getTemp(p, typ.returnType, needsInit=true)
getTemp(p, typ[0], d, needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
discard "resetLoc(p, d)"
params.addArgument(argBuilder):
params.add(addrLoc(p.config, d))
pl.add(addrLoc(p.config, d))
genCallPattern()
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
params.addArgument(argBuilder):
params.add(addrLoc(p.config, tmp))
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
genCallPattern()
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {}) # no need for deep copying
elif isHarmlessStore(p, canRaise, d):
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
let rp = rdLoc(op)
let pars = extract(params)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.snippet = callIter(rp, pars)
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.snippet = callProc(rp, pars, rawProc)
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, d, list, {}) # no need for deep copying
if canRaise: raiseExit(p)
else:
var tmp: TLoc = getTemp(p, typ.returnType)
var tmp: TLoc
getTemp(p, typ[0], tmp)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
let rp = rdLoc(op)
let pars = extract(params)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
if tfIterator in typ.flags:
list.snippet = callIter(rp, pars)
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
else:
list.snippet = callProc(rp, pars, rawProc)
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
genAssignment(p, tmp, list, {})
if canRaise: raiseExit(p)
genAssignment(p, d, tmp, {})
@@ -608,27 +508,30 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
genCallPattern()
if canRaise: raiseExit(p)
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
argBuilder: var CallBuilder) =
if i < typ.n.len:
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
argsCounter: var int) =
if i < typ.len:
# 'var T' is 'T&' in C++. This means we ignore the request of
# any nkHiddenAddr when it's a 'var T'.
let paramType = typ.n[i]
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
discard
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
result.addArgument(argBuilder):
genArgNoParam(p, ri[i][0], result)
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i][0], result)
inc argsCounter
else:
result.addArgument(argBuilder):
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
inc argsCounter
else:
if tfVarargs notin typ.flags:
localError(p.config, ri.info, "wrong argument count")
else:
result.addArgument(argBuilder):
genArgNoParam(p, ri[i], result)
if argsCounter > 0: result.add ", "
genArgNoParam(p, ri[i], result)
inc argsCounter
discard """
Dot call syntax in C++
@@ -685,11 +588,11 @@ proc skipAddrDeref(node: PNode): PNode =
else:
result = node
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
# for better or worse c2nim translates the 'this' argument to a 'var T'.
# However manual wrappers may also use 'ptr T'. In any case we support both
# for convenience.
internalAssert p.config, i < typ.n.len
internalAssert p.config, i < typ.len
assert(typ.n[i].kind == nkSym)
# if the parameter is lying (tyVar) and thus we required an additional deref,
# skip the deref:
@@ -720,15 +623,15 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
genArgNoParam(p, ri, result) #, typ.n[i].sym)
result.add(".")
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) =
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
var i = 0
var j = 1
while i < pat.len:
case pat[i]
of '@':
var callBuilder = default(CallBuilder) # not init call builder
var argsCounter = 0
for k in j..<ri.len:
genOtherArg(p, ri, k, typ, result, callBuilder)
genOtherArg(p, ri, k, typ, result, argsCounter)
inc i
of '#':
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
@@ -738,11 +641,11 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
result.add("(")
if 1 < ri.len:
var callBuilder: CallBuilder = default(CallBuilder)
genOtherArg(p, ri, 1, typ, result, callBuilder)
var argsCounterB = 0
genOtherArg(p, ri, 1, typ, result, argsCounterB)
for k in j+1..<ri.len:
var callBuilder: CallBuilder = default(CallBuilder)
genOtherArg(p, ri, k, typ, result, callBuilder)
var argsCounterB = 0
genOtherArg(p, ri, k, typ, result, argsCounterB)
result.add(")")
else:
localError(p.config, ri.info, "call expression expected for C++ pattern")
@@ -756,15 +659,15 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
genArgNoParam(p, arg, result)
#result.add debugTree(arg, 0, 10)
else:
var callBuilder = default(CallBuilder) # not init call builder
genOtherArg(p, ri, j, typ, result, callBuilder)
var argsCounter = 0
genOtherArg(p, ri, j, typ, result, argsCounter)
inc j
inc i
of '\'':
var idx, stars: int = 0
var idx, stars: int
if scanCppGenericSlot(pat, i, idx, stars):
var t = resolveStarsInCppType(typ, idx, stars)
if t == nil: result.add(CVoid)
if t == nil: result.add("void")
else: result.add(getTypeDesc(p.module, t))
else:
let start = i
@@ -775,61 +678,67 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Bu
result.add(substr(pat, start, i - 1))
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
var op = initLocExpr(p, ri[0])
var op: TLoc
initLocExpr(p, ri[0], op)
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.snippet
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}):
var pl = newBuilder("")
var pl = newRopeAppender()
genPatternCall(p, ri, pat, typ, pl)
# simpler version of 'fixupCall' that works with the pl+params combination:
var typ = skipTypes(ri[0].typ, abstractInst)
if typ.returnType != nil:
if typ[0] != nil:
if p.module.compileToCpp and lfSingleUse in d.flags:
# do not generate spurious temporaries for C++! For C we're better off
# with them to prevent undefined behaviour and because the codegen
# is free to emit expressions multiple times!
d.k = locCall
d.snippet = extract(pl)
d.r = pl
excl d.flags, lfSingleUse
else:
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
list.snippet = extract(pl)
var list: TLoc
initLoc(list, locCall, d.lode, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(pl))
pl.add(";\n")
line(p, cpsStmts, pl)
else:
var pl = newBuilder("")
var pl = newRopeAppender()
var argsCounter = 0
if 1 < ri.len:
genThisArg(p, ri, 1, typ, pl)
pl.add(op.snippet)
var res = newBuilder("")
var call = initCallBuilder(res, extract(pl))
pl.add(op.r)
var params = newRopeAppender()
for i in 2..<ri.len:
genOtherArg(p, ri, i, typ, res, call)
fixupCall(p, le, ri, d, res, call)
assert(typ.len == typ.n.len)
genOtherArg(p, ri, i, typ, params, argsCounter)
fixupCall(p, le, ri, d, pl, params)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call
var op = initLocExpr(p, ri[0])
var pl = newBuilder("[")
var op: TLoc
initLocExpr(p, ri[0], op)
var pl = "["
# getUniqueType() is too expensive here:
var typ = skipTypes(ri[0].typ, abstractInst)
assert(typ.kind == tyProc)
assert(typ.len == typ.n.len)
# don't call '$' here for efficiency:
let pat = $ri[0].sym.loc.snippet
let pat = $ri[0].sym.loc.r
internalAssert p.config, pat.len > 0
var start = 3
if ' ' in pat:
start = 1
pl.add(op.snippet)
pl.add(op.r)
if ri.len > 1:
pl.add(": ")
genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -838,12 +747,13 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
if ri.len > 1:
genArg(p, ri[1], typ.n[1].sym, ri, pl)
pl.add(" ")
pl.add(op.snippet)
pl.add(op.r)
if ri.len > 2:
pl.add(": ")
genArg(p, ri[2], typ.n[2].sym, ri, pl)
for i in start..<ri.len:
if i >= typ.n.len:
assert(typ.len == typ.n.len)
if i >= typ.len:
internalError(p.config, ri.info, "varargs for objective C method?")
assert(typ.n[i].kind == nkSym)
var param = typ.n[i].sym
@@ -851,36 +761,35 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
pl.add(param.name.s)
pl.add(": ")
genArg(p, ri[i], param, ri, pl)
if typ.returnType != nil:
if typ[0] != nil:
if isInvalidReturnType(p.config, typ):
if ri.len > 1: pl.add(" ")
# beware of 'result = p(result)'. We always allocate a temporary:
if d.k in {locTemp, locNone}:
# We already got a temp. Great, special case it:
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
pl.add("Result: ")
pl.add(addrLoc(p.config, d))
pl.add("]")
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(pl))
pl.add("];\n")
line(p, cpsStmts, pl)
else:
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
var tmp: TLoc
getTemp(p, typ[0], tmp, needsInit=true)
pl.add(addrLoc(p.config, tmp))
pl.add("]")
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(pl))
pl.add("];\n")
line(p, cpsStmts, pl)
genAssignment(p, d, tmp, {}) # no need for deep copying
else:
pl.add("]")
if d.k == locNone: d = getTemp(p, typ.returnType)
if d.k == locNone: getTemp(p, typ[0], d)
assert(d.t != nil) # generate an assignment to d:
var list: TLoc = initLoc(locCall, ri, OnUnknown)
list.snippet = extract(pl)
var list: TLoc
initLoc(list, locCall, ri, OnUnknown)
list.r = pl
genAssignment(p, d, list, {}) # no need for deep copying
else:
pl.add("]")
p.s(cpsStmts).addStmt():
p.s(cpsStmts).add(extract(pl))
pl.add("];\n")
line(p, cpsStmts, pl)
proc notYetAlive(n: PNode): bool {.inline.} =
let r = getRoot(n)
@@ -909,16 +818,6 @@ proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri):
return
when defined(icDbgHash):
if ri[0].typ == nil:
echo "NILCALLEE kind=", ri[0].kind,
" sym=", (if ri[0].kind == nkSym: ri[0].sym.name.s else: "-"),
" symKind=", (if ri[0].kind == nkSym: $ri[0].sym.kind else: "-"),
" flags=", (if ri[0].kind == nkSym: $ri[0].sym.flags else: "-"),
" lazy=", nfLazyType in ri[0].flags,
" inProc=", (if p.prc != nil: p.prc.name.s else: "NIL"),
" module=", p.module.module.name.s
raiseAssert "nil callee type, see NILCALLEE above"
if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
genClosureCall(p, le, ri, d)
elif ri[0].kind == nkSym and sfInfixCall in ri[0].sym.flags:

File diff suppressed because it is too large Load Diff

View File

@@ -16,17 +16,16 @@
## implementation.
template detectVersion(field, corename) =
if m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
result = 2
else:
result = 1
if m.g.field == 0:
let core = getCompilerProc(m.g.graph, corename)
if core == nil or core.kind != skConst:
m.g.field = 1
else:
m.g.field = toInt(ast.getInt(core.astdef))
result = m.g.field
proc detectStrVersion(m: BModule): int =
if m.g.config.usesSso() and
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
result = 3
else:
detectVersion(strVersion, "nimStrVersion")
detectVersion(strVersion, "nimStrVersion")
proc detectSeqVersion(m: BModule): int =
detectVersion(seqVersion, "nimSeqVersion")
@@ -37,84 +36,52 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
cgsym(m, "TGenericSeq")
let tmp = getTempName(m)
result.add tmp
var res = newBuilder("")
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "Sup", typ = "TGenericSeq")
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var strInit: StructInitializer
res.addStructInitializer(strInit, kind = siOrderedStruct):
res.addField(strInit, name = "Sup"):
var seqInit: StructInitializer
res.addStructInitializer(seqInit, kind = siOrderedStruct):
res.addField(seqInit, name = "len"):
res.addIntValue(s.len)
res.addField(seqInit, name = "reserved"):
res.add(cCast(NimInt, cOp(BitOr, NimUint, cCast(NimUint, cIntValue(s.len)), NimStrlitFlag)))
res.addField(strInit, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
[tmp, makeCString(s), rope(s.len)])
proc genStringLiteralV1(m: BModule; n: PNode; result: var Builder) =
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
if s.isNil:
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
else:
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var name: string = ""
if id == m.labels:
# string literal not found in the cache:
genStringLiteralDataOnlyV1(m, n.strVal, name)
appcg(m, result, "((#NimStringDesc*) &", [])
genStringLiteralDataOnlyV1(m, n.strVal, result)
result.add ")"
else:
name = m.tmpBase & $id
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name)))
appcg(m, result, "((#NimStringDesc*) &$1$2)",
[m.tmpBase, id])
# ------ Version 2: destructor based strings and seqs -----------------------
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
var res = newBuilder("")
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = result):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "cap", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var structInit: StructInitializer
res.addStructInitializer(structInit, kind = siOrderedStruct):
res.addField(structInit, name = "cap"):
res.add(cOp(BitOr, NimInt, cIntValue(s.len), NimStrlitFlag))
res.addField(structInit, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
m.s[cfsStrData].addf("static $4 struct {$n" &
" NI cap; NIM_CHAR data[$2+1];$n" &
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
[result, rope(s.len), makeCString(s),
rope(if isConst: "const" else: "")])
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder) =
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var litName: string
if id == m.labels:
let pureLit = getTempName(m)
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
let tmp = getTempName(m)
result.add tmp
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
# string literal not found in the cache:
litName = getTempName(m)
genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst)
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
else:
litName = m.tmpBase & $id
let tmp = getTempName(m)
result.add tmp
var res = newBuilder("")
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp,
typ = "NimStringV2"):
var strInit: StructInitializer
res.addStructInitializer(strInit, kind = siOrderedStruct):
res.addField(strInit, name = "len"):
res.addIntValue(n.strVal.len)
res.addField(strInit, name = "p"):
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
m.s[cfsStrData].add(extract(res))
let tmp = getTempName(m)
result.add tmp
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
rope(if isConst: "const" else: "")])
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var pureLit: Rope
if id == m.labels:
@@ -125,198 +92,7 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Bu
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
else:
pureLit = m.tmpBase & rope(id)
var strInit: StructInitializer
result.addStructInitializer(strInit, kind = siOrderedStruct):
result.addField(strInit, name = "len"):
result.addIntValue(n.strVal.len)
result.addField(strInit, name = "p"):
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
proc ssoCharLit(ch: char): string =
## Return a C char literal for ch, with proper escaping.
const hexDigits = "0123456789abcdef"
result = "'"
case ch
of '\'': result.add("\\'")
of '\\': result.add("\\\\")
of '\0': result.add("\\0")
of '\n': result.add("\\n")
of '\r': result.add("\\r")
of '\t': result.add("\\t")
elif ch.ord < 32 or ch.ord == 127:
result.add("\\x")
result.add(hexDigits[ch.ord shr 4])
result.add(hexDigits[ch.ord and 0xf])
else:
result.add(ch)
result.add('\'')
proc ssoBytesLit(m: BModule; s: string; slen: int): string =
## Compute the `bytes` field value for the new SmallString layout.
## byte 0 = slen, bytes 1-7 = inline chars 0-6 (zero-padded).
## On LE: slen in bits 0-7, char[i] in bits (i+1)*8..(i+1)*8+7.
## On BE: slen in bits 56-63, char[i] in bits (6-i)*8..(6-i)*8+7.
const AlwaysAvail = 7
var val: uint64
if CPU[m.g.config.target.targetCPU].endian == littleEndian:
val = uint64(slen)
for i in 0..<min(s.len, AlwaysAvail):
val = val or (uint64(s[i]) shl (uint(i + 1) * 8))
else:
val = uint64(slen) shl 56
for i in 0..<min(s.len, AlwaysAvail):
val = val or (uint64(s[i]) shl (uint(AlwaysAvail - 1 - i) * 8))
# Cast to NU (C name for Nim's uint, = NU64 on 64-bit). NU64 = uint64_t.
result = cCast("NU", $val & "ULL")
proc ssoMoreLit(m: BModule; s: string): string =
## For medium string literals (AlwaysAvail < len <= PayloadSize), encode
## chars[AlwaysAvail..ptrSize-1] in the 'more' pointer field bit-pattern.
## The last pointer byte is always '\0' (null terminator), guaranteed by
## PayloadSize = AlwaysAvail + ptrSize - 1. slen <= PayloadSize guards
## prevent any code from dereferencing this as an actual pointer.
const AlwaysAvail = 7
let ptrSize = m.g.config.target.ptrSize
var val: uint64 = 0
for i in 0..<ptrSize:
let ch: uint64 = if AlwaysAvail + i < s.len: uint64(s[AlwaysAvail + i]) else: 0
if CPU[m.g.config.target.targetCPU].endian == littleEndian:
val = val or (ch shl (uint(i) * 8))
else:
val = val or (ch shl (uint(ptrSize - 1 - i) * 8))
result = cCast(ptrType("LongString"), "(uintptr_t)" & $val)
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
# Inline SmallString struct initializer for use inside const aggregate types.
# Layout: {bytes: NimUint, more: ptr LongString}
# bytes = slen (low byte) | char[0]<<8 | char[1]<<16 | ... | char[6]<<56
const AlwaysAvail = 7
let s = n.strVal
cgsym(m, "SmallString")
cgsym(m, "LongString")
let payloadSize = AlwaysAvail + m.g.config.target.ptrSize - 1
var si: StructInitializer
result.addStructInitializer(si, kind = siOrderedStruct):
if s.len <= AlwaysAvail:
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, s.len))
result.addField(si, name = "more"):
result.add(NimNil)
elif s.len <= payloadSize:
# Medium string: bytes holds slen + chars 0-6; more holds chars 7..PayloadSize-1.
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, s.len))
result.addField(si, name = "more"):
result.add(ssoMoreLit(m, s))
else:
# Emit the LongString block into cfsStrData and reference it inline.
let dataName = getTempName(m)
var res = newBuilder("")
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = dataName):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "rc", typ = NimInt)
res.addField(name = "fullLen", typ = NimInt)
res.addField(name = "capImpl", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var di: StructInitializer
res.addStructInitializer(di, kind = siOrderedStruct):
res.addField(di, name = "fullLen"):
res.addIntValue(s.len)
res.addField(di, name = "rc"):
res.addIntValue(1)
res.addField(di, name = "capImpl"):
res.addIntValue(0) # static, never freed
res.addField(di, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
# slen = StaticSlen (254): marks this as a static (never-freed) long string.
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, 254))
result.addField(si, name = "more"):
result.add(cCast(ptrType("LongString"), cAddr(dataName)))
# ------ Version 3: SmallString (SSO) strings --------------------------------
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder) =
# SmallString literal. Always generate a fresh SmallString variable (like v2
# always generates a fresh outer NimStringV2). For long strings, cache the
# LongString payload to avoid duplicates within a module.
const AlwaysAvail = 7 # must match strs_v3.nim
let s = n.strVal
let tmp = getTempName(m)
result.add tmp
cgsym(m, "SmallString")
cgsym(m, "LongString")
let payloadSize = AlwaysAvail + m.g.config.target.ptrSize - 1
var res = newBuilder("")
if s.len <= AlwaysAvail:
# Short: bytes holds slen + all chars (zero-padded), more = NULL.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, s.len))
res.addField(si, name = "more"):
res.add(NimNil)
elif s.len <= payloadSize:
# Medium: bytes holds slen + chars 0-6; more holds chars 7..PayloadSize-1 as raw bits.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, s.len))
res.addField(si, name = "more"):
res.add(ssoMoreLit(m, s))
else:
# Long: cache the LongString block to emit it only once per module per string.
# Always generate a fresh SmallString pointing at the (possibly cached) block.
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var dataName: string
if id == m.labels:
dataName = getTempName(m)
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = dataName):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "rc", typ = NimInt)
res.addField(name = "fullLen", typ = NimInt)
res.addField(name = "capImpl", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var di: StructInitializer
res.addStructInitializer(di, kind = siOrderedStruct):
res.addField(di, name = "fullLen"):
res.addIntValue(s.len)
res.addField(di, name = "rc"):
res.addIntValue(1)
res.addField(di, name = "capImpl"):
res.addIntValue(0) # bit 0 = 0: static, never freed
res.addField(di, name = "data"):
res.add(makeCString(s))
else:
dataName = m.tmpBase & $id
# slen = StaticSlen (254): marks this as a static (never-freed) long string.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, 254))
res.addField(si, name = "more"):
res.add(cCast(ptrType("LongString"), cAddr(dataName)))
m.s[cfsStrData].add(extract(res))
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
# ------ Version selector ---------------------------------------------------
@@ -328,18 +104,15 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
let tmp = getTempName(m)
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
result.add tmp
of 3:
localError(m.config, info, "genStringLiteralDataOnly not supported for SmallString (nimsso)")
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Builder) =
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
case detectStrVersion(m)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)
of 3: genStringLiteralV3(m, n, isConst = true, result)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View File

@@ -24,31 +24,28 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
of nkRecCase:
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
let disc = n[0].sym
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
if disc.loc.r == "": fillObjectFields(p.module, typ)
if disc.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
let discField = dotField(accessor, disc.loc.snippet)
p.s(cpsStmts).addSwitchStmt(discField):
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
var caseBuilder: SwitchCaseBuilder
p.s(cpsStmts).addSwitchCase(caseBuilder):
if branch.kind == nkOfBranch:
genCaseRange(p, branch, caseBuilder)
else:
p.s(cpsStmts).addCaseElse(caseBuilder)
do:
specializeResetN(p, accessor, lastSon(branch), typ)
p.s(cpsStmts).addBreak()
specializeResetT(p, discField, disc.loc.t)
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
if branch.kind == nkOfBranch:
genCaseRange(p, branch)
else:
lineF(p, cpsStmts, "default:$n", [])
specializeResetN(p, accessor, lastSon(branch), typ)
lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n", [])
specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.snippet == "": fillObjectFields(p.module, typ)
if field.loc.r == "": fillObjectFields(p.module, typ)
if field.loc.t == nil:
internalError(p.config, n.info, "specializeResetN()")
specializeResetT(p, dotField(accessor, field.loc.snippet), field.loc.t)
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(p.config, n.info, "specializeResetN()")
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
@@ -57,62 +54,53 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink, tyOwned:
specializeResetT(p, accessor, skipModifier(typ))
specializeResetT(p, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(p.config, typ.indexType)
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
specializeResetT(p, subscript(accessor, i.snippet), typ.elementType)
let arraySize = lengthOrd(p.config, typ[0])
var i: TLoc
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, arraySize])
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ[1])
lineF(p, cpsStmts, "}$n", [])
of tyObject:
var x = typ.baseClass
if x != nil: x = x.skipTypes(skipPtrs)
specializeResetT(p, accessor.parentObj(p.module), x)
if typ.n != nil:
if typ.sym != nil and sfImportc in typ.sym.flags:
# imported C struct, nimZeroMem
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
cCast(ptrType(CPointer), cAddr(accessor)),
cSizeof(getTypeDesc(p.module, typ)))
else:
specializeResetN(p, accessor, typ.n, typ)
for i in 0..<typ.len:
var x = typ[i]
if x != nil: x = x.skipTypes(skipPtrs)
specializeResetT(p, accessor.parentObj(p.module), x)
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
specializeResetT(p, dotField(accessor, "Field" & $i), a)
for i in 0..<typ.len:
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
of tyString, tyRef, tySequence:
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
cCast(ptrType(CPointer), cAddr(accessor)),
NimNil)
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
of tyProc:
if typ.callConv == ccClosure:
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
cCast(ptrType(CPointer), cAddr(dotField(accessor, "ClE_0"))),
NimNil)
p.s(cpsStmts).addFieldAssignment(accessor, "ClP_0", NimNil)
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
else:
p.s(cpsStmts).addAssignment(accessor, NimNil)
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
of tyChar, tyBool, tyEnum, tyInt..tyUInt64:
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
p.s(cpsStmts).addAssignment(accessor, NimNil)
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
of tySet:
case mapSetType(p.config, typ)
of ctArray:
let t = getTypeDesc(p.module, typ)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
accessor,
cSizeof(t))
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
[accessor, getTypeDesc(p.module, typ)])
of ctInt8, ctInt16, ctInt32, ctInt64:
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
else:
raiseAssert "unexpected set type kind"
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
doAssert false, "unexpected set type kind"
of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
discard
proc specializeReset(p: BProc, a: TLoc) =

File diff suppressed because it is too large Load Diff

View File

@@ -19,12 +19,9 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
p.flags.incl threadVarAccessed
incl p.module.flags, usesThreadVars
p.procSec(cpsLocals).addVar(kind = Local,
name = "NimTV_",
typ = ptrType("NimThreadVars"))
p.procSec(cpsInit).addAssignment("NimTV_",
cCast(ptrType("NimThreadVars"),
cCall(cgsymValue(p.module, "GetThreadLocalVars"))))
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
p.procSec(cpsInit).add(
ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
if emulatedThreadVars(m.config):
@@ -33,51 +30,30 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
# allocator for it :-(
if not containsOrIncl(m.g.nimtvDeclared, s.id):
m.g.nimtvDeps.add(s.loc.t)
m.g.nimtv.addField(name = s.loc.snippet, typ = getTypeDesc(m, s.loc.t))
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
else:
let vis =
if isExtern: Extern
elif lfExportLib in s.loc.flags: ExportLibVar
else: Private
if m.config.cmd == cmdNifC and vis == Private and not isExtern:
# A `{.threadvar.}`/`{.global.}` thread-local declared inside a routine is
# emitted by every module that emit-everywhere's its enclosing routine
# (e.g. libp2p's `var keys {.global.}: HashSet`), so its content-addressed
# name collides at link. Same fix as a plain global (genGlobalVarDecl):
# `extern` declaration + a droppable `'d'` definition unit the merge stage
# assigns one owner. The thread-local storage class rides on both.
let cname = stripCnifMarks(s.loc.snippet)
let td = getTypeDesc(m, s.loc.t)
# `extern` declaration via the full `addVar` overload — it knows the
# thread-local storage class (`NIM_THREADVAR`); the simple `addVar`'s
# `addVarHeader` does not implement `Threadvar`.
m.s[cfsVars].addVar(m, s, name = s.loc.snippet, typ = td,
kind = Threadvar, visibility = Extern)
m.s[cfsVars].add(cnifDefDirective(cname, "d", icNifName(m, s)))
m.s[cfsVars].addVar(m, s,
name = s.loc.snippet, typ = td, kind = Threadvar, visibility = vis)
m.s[cfsVars].add(cnifEndDefs())
else:
m.s[cfsVars].addVar(m, s,
name = s.loc.snippet,
typ = getTypeDesc(m, s.loc.t),
kind = Threadvar,
visibility = vis)
if isExtern: m.s[cfsVars].add("extern ")
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
else: m.s[cfsVars].add("N_LIB_PRIVATE ")
if optThreads in m.config.globalOptions:
let sym = s.typ.sym
if sym != nil and sfCppNonPod in sym.flags:
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
else: m.s[cfsVars].add("NIM_THREADVAR ")
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
proc generateThreadLocalStorage(m: BModule) =
if m.g.nimtv.buf.len != 0 and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
finishTypeDescriptions(m)
m.s[cfsSeqTypes].addTypedef(name = "NimThreadVars"):
m.s[cfsSeqTypes].addSimpleStruct(m, name = "", baseType = ""):
m.s[cfsSeqTypes].add(extract(m.g.nimtv))
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
proc generateThreadVarsSize(m: BModule) =
if m.g.nimtv.buf.len != 0:
if m.g.nimtv != "":
let externc = if m.config.backend == backendCpp or
sfCompileToCpp in m.module.flags: ExternC
else: None
m.s[cfsProcs].addDeclWithVisibility(externc):
m.s[cfsProcs].addProcHeader("NimThreadVarsSize", NimInt, cProcParams())
m.s[cfsProcs].finishProcHeaderWithBody():
m.s[cfsProcs].addReturn(cCast(NimInt, cSizeof("NimThreadVars")))
sfCompileToCpp in m.module.flags: "extern \"C\" "
else: ""
m.s[cfsProcs].addf(
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
[externc.rope])

View File

@@ -16,15 +16,12 @@ type
p: BProc
visitorFrmt: string
const
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder)
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
proc visit(p: BProc, data, visitor: Snippet) =
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisit"),
cCast(CPointer, data),
visitor)
proc genCaseRange(p: BProc, branch: PNode)
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
typ: PType) =
@@ -37,35 +34,32 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
let disc = n[0].sym
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
if disc.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
let discField = dotField(accessor, disc.loc.snippet)
p.s(cpsStmts).addSwitchStmt(discField):
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
var caseBuilder: SwitchCaseBuilder
p.s(cpsStmts).addSwitchCase(caseBuilder):
if branch.kind == nkOfBranch:
genCaseRange(c.p, branch, caseBuilder)
else:
p.s(cpsStmts).addCaseElse(caseBuilder)
do:
genTraverseProc(c, accessor, lastSon(branch), typ)
p.s(cpsStmts).addBreak()
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in 1..<n.len:
let branch = n[i]
assert branch.kind in {nkOfBranch, nkElse}
if branch.kind == nkOfBranch:
genCaseRange(c.p, branch)
else:
lineF(p, cpsStmts, "default:$n", [])
genTraverseProc(c, accessor, lastSon(branch), typ)
lineF(p, cpsStmts, "break;$n", [])
lineF(p, cpsStmts, "} $n", [])
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
if field.loc.r == "": fillObjectFields(c.p.module, typ)
if field.loc.t == nil:
internalError(c.p.config, n.info, "genTraverseProc()")
genTraverseProc(c, dotField(accessor, field.loc.snippet), field.loc.t)
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(c.p.config, n.info, "genTraverseProc()")
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
if not m.compileToCpp:
result = dotField(accessor, "Sup")
result = "$1.Sup" % [accessor]
else:
result = accessor
@@ -77,156 +71,128 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink, tyOwned:
genTraverseProc(c, accessor, skipModifier(typ))
genTraverseProc(c, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(c.p.config, typ.indexType)
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
let arraySize = lengthOrd(c.p.config, typ[0])
var i: TLoc
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
var oldCode = p.s(cpsStmts)
var oldLen, newLen: int
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
oldLen = p.s(cpsStmts).buf.len
genTraverseProc(c, subscript(accessor, i.snippet), typ.elementType)
newLen = p.s(cpsStmts).buf.len
if oldLen == newLen:
freeze oldCode
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, arraySize])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ[1])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
else:
lineF(p, cpsStmts, "}$n", [])
of tyObject:
var x = typ.baseClass
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
for i in 0..<typ.len:
var x = typ[i]
if x != nil: x = x.skipTypes(skipPtrs)
genTraverseProc(c, accessor.parentObj(c.p.module), x)
if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
of tyTuple:
let typ = getUniqueType(typ)
for i, a in typ.ikids:
genTraverseProc(c, dotField(accessor, "Field" & $i), a)
for i in 0..<typ.len:
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
of tyRef:
visit(p, accessor, c.visitorFrmt)
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
of tySequence:
if optSeqDestructors notin c.p.module.config.globalOptions:
visit(p, accessor, c.visitorFrmt)
elif containsGarbageCollectedRef(typ.elementType):
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
elif containsGarbageCollectedRef(typ.lastSon):
# destructor based seqs are themselves not traced but their data is, if
# they contain a GC'ed type:
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisitSeq"),
cCast(CPointer, accessor),
c.visitorFrmt)
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
#genTraverseProcSeq(c, accessor, typ)
of tyString:
if tfHasAsgn notin typ.flags:
visit(p, accessor, c.visitorFrmt)
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
of tyProc:
if typ.callConv == ccClosure:
visit(p, dotField(accessor, "ClE_0"), c.visitorFrmt)
lineCg(p, cpsStmts, visitorFrmt, [ropecg(c.p.module, "$1.ClE_0", [accessor]), c.visitorFrmt])
else:
discard
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p
assert typ.kind == tySequence
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
var i: TLoc
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
var oldCode = p.s(cpsStmts)
var oldLen, newLen: int
var a = TLoc(snippet: accessor)
let le = lenExpr(c.p, a)
freeze oldCode
var a: TLoc
a.r = accessor
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), le):
oldLen = p.s(cpsStmts).buf.len
genTraverseProc(c, subscript(dataField(c.p, accessor), i.snippet), typ.elementType)
newLen = p.s(cpsStmts).buf.len
if newLen == oldLen:
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
[i.r, lenExpr(c.p, a)])
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
else:
lineF(p, cpsStmts, "}$n", [])
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
var c: TTraversalClosure
var p = newProc(nil, m)
result = "Marker_" & getTypeName(m, origTyp, sig)
let
hcrOn = m.hcrOn
typ = origTyp.skipTypes(abstractInstOwned)
markerName = if hcrOn: result & "_actual" else: result
header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
t = getTypeDesc(m, typ)
p.s(cpsLocals).addVar(kind = Local, name = "a", typ = t)
p.s(cpsInit).addAssignment("a", cCast(t, "p"))
lineF(p, cpsLocals, "$1 a;$n", [t])
lineF(p, cpsInit, "a = ($1)p;$n", [t])
var c = TTraversalClosure(p: p,
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
)
c.p = p
c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
assert typ.kind != tyTypeDesc
if typ.kind == tySequence:
genTraverseProcSeq(c, "a".rope, typ)
else:
if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
# C's arrays are broken beyond repair:
genTraverseProc(c, "a".rope, typ.elementType)
genTraverseProc(c, "a".rope, typ[0])
else:
genTraverseProc(c, cDeref("a"), typ.elementType)
genTraverseProc(c, "(*a)".rope, typ[0])
var headerBuilder = newBuilder("")
headerBuilder.addProcHeaderWithParams(ccNimCall, markerName, CVoid):
var paramBuilder: ProcParamBuilder
headerBuilder.addProcParams(paramBuilder):
headerBuilder.addParam(paramBuilder, name = "p", typ = CPointer)
headerBuilder.addParam(paramBuilder, name = "op", typ = NimInt)
let header = extract(headerBuilder)
let generatedProc = "$1 {$n$2$3$4}\n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
m.s[cfsProcHeaders].add(header)
m.s[cfsProcHeaders].finishProcHeaderAsProto()
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
m.s[cfsProcs].add(header)
m.s[cfsProcs].finishProcHeaderWithBody():
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
m.s[cfsProcs].add(extract(p.s(cpsInit)))
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
m.s[cfsProcHeaders].addf("$1;\n", [header])
m.s[cfsProcs].add(generatedProc)
if hcrOn:
var desc = newBuilder("")
var unnamedParamBuilder: ProcParamBuilder
desc.addProcParams(unnamedParamBuilder):
desc.addUnnamedParam(unnamedParamBuilder, CPointer)
desc.addUnnamedParam(unnamedParamBuilder, NimInt)
let unnamedParams = extract(desc)
m.s[cfsProcHeaders].addProcVar(ccNimCall, result, unnamedParams, CVoid)
m.s[cfsDynLibInit].addAssignmentWithValue(result):
m.s[cfsDynLibInit].addCast(procPtrTypeUnnamed(ccNimCall, CVoid, unnamedParams)):
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
getModuleDllPath(m),
'"' & result & '"',
cCast(CPointer, markerName))
m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
[result, markerName, getModuleDllPath(m)])
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
discard genTypeInfoV1(m, s.loc.t, info)
var c: TTraversalClosure
var p = newProc(nil, m)
var sLoc = rdLoc(s.loc)
result = getTempName(m)
if sfThread in s.flags and emulatedThreadVars(m.config):
accessThreadLocalVar(p, s)
sLoc = derefField("NimTV_", sLoc)
var c = TTraversalClosure(p: p,
visitorFrmt: cIntValue(0) # "#nimGCvisit((void*)$1, 0);$n"
)
sLoc = "NimTV_->" & sLoc
c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
c.p = p
let header = "static N_NIMCALL(void, $1)(void)" % [result]
genTraverseProc(c, sLoc, s.loc.t)
var headerBuilder = newBuilder("")
headerBuilder.addProcHeaderWithParams(ccNimCall, result, CVoid):
var paramBuilder: ProcParamBuilder
headerBuilder.addProcParams(paramBuilder):
# (void)
discard
let header = extract(headerBuilder)
let generatedProc = "$1 {$n$2$3$4}$n" %
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
m.s[cfsProcHeaders].add(header)
m.s[cfsProcHeaders].finishProcHeaderAsProto()
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
m.s[cfsProcs].add(header)
m.s[cfsProcs].finishProcHeaderWithBody():
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
m.s[cfsProcs].add(extract(p.s(cpsInit)))
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].add(generatedProc)

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,8 @@
# This module declares some helpers for the C code generator.
import
ast, types, msgs, wordrecg,
platform, trees, options, cgendata, mangleutils, renderer, modulegraphs
import std/[hashes, strutils, formatfloat]
ast, types, hashes, strutils, msgs, wordrecg,
platform, trees, options, cgendata
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -21,16 +19,13 @@ when defined(nimPreviewSlimSystem):
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind
of nkStmtList:
result = nil
for i in 0..<n.len:
result = getPragmaStmt(n[i], w)
if result != nil: break
of nkPragma:
result = nil
for i in 0..<n.len:
if whichPragma(n[i]) == w: return n[i]
else:
result = nil
else: discard
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
result = getPragmaStmt(n, w) != nil
@@ -58,7 +53,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt =
a = a + (a shl 3)
a = a xor (a shr 11)
a = a + (a shl 15)
result = cast[Hash](uint(a))
result = cast[Hash](a)
template getUniqueType*(key: PType): PType = key
@@ -68,6 +63,53 @@ proc makeSingleLineCString*(s: string): string =
c.toCChar(result)
result.add('\"')
proc mangle*(name: string): string =
result = newStringOfCap(name.len)
var start = 0
if name[0] in Digits:
result.add("X" & name[0])
start = 1
var requiresUnderscore = false
template special(x) =
result.add x
requiresUnderscore = true
for i in start..<name.len:
let c = name[i]
case c
of 'a'..'z', '0'..'9', 'A'..'Z':
result.add(c)
of '_':
# we generate names like 'foo_9' for scope disambiguations and so
# disallow this here:
if i > 0 and i < name.len-1 and name[i+1] in Digits:
discard
else:
result.add(c)
of '$': special "dollar"
of '%': special "percent"
of '&': special "amp"
of '^': special "roof"
of '!': special "emark"
of '?': special "qmark"
of '*': special "star"
of '+': special "plus"
of '-': special "minus"
of '/': special "slash"
of '\\': special "backslash"
of '=': special "eq"
of '<': special "lt"
of '>': special "gt"
of '~': special "tilde"
of ':': special "colon"
of '.': special "dot"
of '@': special "at"
of '|': special "bar"
else:
result.add("X" & toHex(ord(c), 2))
requiresUnderscore = true
if requiresUnderscore:
result.add "_"
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
case int(getSize(conf, typ))
of 1: result = ctInt8
@@ -80,10 +122,7 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
var pt = skipTypes(s.typ, typedescInst)
assert skResult != s.kind
#note precedence: params override types
if optByRef in s.options: return true
elif sfByCopy in s.flags: return false
elif tfByRef in pt.flags: return true
if tfByRef in pt.flags: return true
elif tfByCopy in pt.flags: return false
case pt.kind
of tyObject:
@@ -106,91 +145,3 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
proc encodeName*(name: string): string =
result = mangle(name)
result = $result.len & result
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
result = if name == "": s.name.s else: name
# keep backend-minted ids out of the `_u` namespace; their item counter
# restarts at 0 and would collide with loaded symbols' ids
if s.itemId.isBackendMinted:
result.add "_c"
if (s.disamb and HookDisambBit) != 0'i32:
# A backend-minted sym whose `disamb` is content-derived (setHookDisamb gave
# it HookDisambBit) — e.g. the `rttiDestroy` wrapper. Its `itemId.item` is a
# PER-PROCESS backend counter, so using it makes the C name diverge across
# the emit-everywhere processes: the type's RTTI table (emit-everywhere,
# merge-deduped) ends up referencing one process's `_c<item>` while the
# wrapper is defined with another's -> undefined at link (`rttiDestroy_c23`).
# The content-derived disamb is stable across processes, so use it.
result.add $s.disamb
else:
result.add $s.itemId.item
else:
result.add "_u"
# Mirror `mangleProcNameExt`: use the per-(module,name) `disamb`, NOT
# `itemId.item`. Under the per-module IC backend the same symbol is loaded
# from a NIF in many processes and `itemId.item` is a fresh, load-order
# dependent counter — so a method base would mangle to `_u1` in one module,
# `_u3` in another and clean at its owner, none of which link. `disamb` is
# assigned deterministically per (module, name) and is serialized, so every
# process that touches the symbol derives the identical C name.
result.add $s.disamb
# module suffix LAST (a strippable trailing token; see `mangleProcNameExt`)
result.add "__"
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
#Module::Type
var name = s.name.s & extra
if makeUnique:
name = makeUnique(m, s, name)
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
result = ""
var kindName = ($t.kind)[2..^1]
kindName[0] = toLower($kindName[0])[0]
case t.kind
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
result = encodeSym(m, t.sym)
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
result = encodeName(t[0].sym.name.s)
result.add "I"
for i in 1..<t.len - 1:
result.add encodeType(m, t[i], staticLists)
result.add "E"
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
result =
case t.kind:
of tySequence: encodeName("seq")
else: encodeName(kindName)
result.add "I"
for i in 0..<t.len:
let s = t[i]
if s.isNil: continue
result.add encodeType(m, s, staticLists)
result.add "E"
of tyStatic:
if t.n != nil:
staticLists.add "_s" & renderTree(t.n)
else:
raiseAssert "unreachable"
of tyRange:
var val = "range_"
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
val.addFloat t.n[0].floatVal
val.add "_"
val.addFloat t.n[1].floatVal
else:
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
result = encodeName(val)
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
result = encodeName(kindName)
of tyAlias, tyInferred, tyOwned:
result = encodeType(m, t.elementType, staticLists)
else:
assert false, "encodeType " & $t.kind

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,8 @@
## This module contains the data structures for the C code generation phase.
import
ast, ropes, options,
lineinfos, pathutils, modulegraphs, cbuilderbase
import std/[intsets, tables, sets]
ast, ropes, options, intsets,
tables, ndi, lineinfos, pathutils, modulegraphs, sets
type
TLabel* = Rope # for the C generator a label is just a rope
@@ -43,12 +41,12 @@ type
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
ctCString
TCFileSections* = array[TCFileSection, Builder] # represents a generated C file
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
cpsInit, # section for init of variables for C proc
cpsStmts # section of local statements for C proc
TCProcSections* = array[TCProcSection, Builder] # represents a generated C proc
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
BModule* = ref TCGen
BProc* = ref TCProc
TBlock* = object
@@ -75,13 +73,10 @@ type
flags*: set[TCProcFlag]
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
currLineInfo*: TLineInfo # AST codegen will make this superfluous
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]]
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
# in how many nested try statements we are
# (the vars must be volatile then)
# `inExcept` is true when we are in the except part of a try block.
# `isHidden` is true for compiler-injected `nkHiddenTryStmt` wrappers
# (e.g. ARC's destructor try/finally around `except T as e:` bodies);
# finallyActions walks past such wrappers to reach the user's try.
# bool is true when are in the except part of a try block
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
# using return in finally statements
labels*: Natural # for generating unique labels in the C proc
@@ -91,7 +86,6 @@ type
options*: TOptions # options that should be used for code
# generation; this is the same as prc.options
# unless prc == nil
optionsStack*: seq[(TOptions, TNoteKinds)]
module*: BModule # used to prevent excessive parameter passing
withinLoop*: int # > 0 if we are within a loop
splitDecls*: int # > 0 if we are in some context for C++ that
@@ -118,11 +112,11 @@ type
# computing alive data on our own.
BModuleList* = ref object of RootObj
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Builder
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
mapping*: Rope # the generated mapping file (if requested)
mods*: seq[BModule] # list of all compiled modules
modules*: seq[BModule] # list of all compiled modules
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
forwardedProcs*: seq[PSym] # procs that did not yet have a body
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
generatedHeader*: BModule
typeInfoMarker*: TypeCacheWithOwner
typeInfoMarkerV2*: TypeCacheWithOwner
@@ -130,7 +124,7 @@ type
graph*: ModuleGraph
strVersion*, seqVersion*: int # version of the string/seq implementation to use
nimtv*: Builder # Nim thread vars; the struct body
nimtv*: Rope # Nim thread vars; the struct body
nimtvDeps*: seq[PType] # type deps: every module needs whole struct
nimtvDeclared*: IntSet # so that every var/field exists only once
# in the struct
@@ -140,7 +134,6 @@ type
# unconditionally...
# nimtvDeps is VERY hard to cache because it's
# not a list of IDs nor can it be made to be one.
mangledPrcs*: HashSet[string]
TCGen = object of PPassContext # represents a C source file
s*: TCFileSections # sections of the C file
@@ -154,83 +147,59 @@ type
typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
# would be ideal but for some reason enums
# don't seem to get cached so it'd generate
# 1 ABI check per occurrence in code
# 1 ABI check per occurence in code
forwTypeCache*: TypeCache # cache for forward declarations of types
declaredThings*: IntSet # things we have declared in this .c file
declaredProtos*: IntSet # prototypes we have declared in this .c file
emittedContentDefs*: HashSet[string]
# cmdNifC per-module backend: content-addressed C names (generic
# instances and synthesized hooks) whose body this TU already emitted.
# Distinct symbols (minted in different source modules) can share one
# `_i<disamb>` name; `declaredThings` keys on symbol id and lets the
# second one through, so we dedup the body by name here instead.
queue*: seq[PSym] # queue of procs to generate
alive*: IntSet # symbol IDs of alive data as computed by `dce.nim`
headerFiles*: seq[string] # needed headers to include
typeInfoMarker*: TypeCache # needed for generating type information
typeInfoMarkerV2*: TypeCache
initProc*: BProc # code for init procedure
preInitProc*: BProc # code executed before the init proc
hcrCreateTypeInfosProc*: Builder # type info globals are in here when HCR=on
hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
hcrInitGuard*: IfBuilder
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable
typeNodes*, nimTypes*: int # used for type info generation
typeNodesName*, nimTypesName*: Rope # used for type info generation
labels*: Natural # for generating unique module-scope names
extensionLoaders*: array['0'..'9', Builder] # special procs for the
extensionLoaders*: array['0'..'9', Rope] # special procs for the
# OpenGL wrapper
sigConflicts*: CountTable[SigHash]
icImplMods*: IntSet # module ids whose routine BODIES this TU
# embeds (redirected defs, shared instances,
# hooks); recorded as the artifact's cdeps so
# the reuse gate can check their impl cookies
icDataDefs*: seq[tuple[cname, nifname: string]]
# C names of data definitions (consts, globals,
# RTTI) this TU embeds plus their NIF symbol
# names (empty for RTTI, which has no symbol);
# recorded in the cnif artifact so a later run
# can reuse the TU and re-demand definitions
# that cached TUs still reference
g*: BModuleList
ndi*: NdiFile
template config*(m: BModule): ConfigRef = m.g.config
template config*(p: BProc): ConfigRef = p.module.g.config
template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
proc delayedCodegen*(m: BModule): bool {.inline.} =
useAliveDataFromDce in m.flags or m.config.globalOptions.contains(optCompress)
proc includeHeader*(this: BModule; header: string) =
if not this.headerFiles.contains header:
this.headerFiles.add header
proc s*(p: BProc, s: TCProcSection): var Builder {.inline.} =
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# section in the current block
result = p.blocks[^1].sections[s]
proc procSec*(p: BProc, s: TCProcSection): var Builder {.inline.} =
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# top level proc sections
result = p.blocks[0].sections[s]
proc initBlock*(): TBlock =
result = TBlock()
for i in low(result.sections)..high(result.sections):
result.sections[i] = newBuilder("")
result.sections[i] = newRopeAppender()
proc newProc*(prc: PSym, module: BModule): BProc =
result = BProc(
prc: prc,
module: module,
optionsStack: if module.initProc != nil: module.initProc.optionsStack
else: @[],
options: if prc != nil: prc.options
else: module.config.options,
blocks: @[initBlock()],
sigConflicts: initCountTable[string]())
if optQuirky in result.options:
result.flags = {nimErrorFlagDisabled}
new(result)
result.prc = prc
result.module = module
result.options = if prc != nil: prc.options
else: module.config.options
result.blocks = @[initBlock()]
result.nestedTryStmts = @[]
result.finallySafePoints = @[]
result.sigConflicts = initCountTable[string]()
proc newModuleList*(g: ModuleGraph): BModuleList =
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),

View File

@@ -10,15 +10,12 @@
## This module implements code generation for methods.
import
options, ast, msgs, idents, renderer, types, magicsys,
sempass2, modulegraphs, lineinfos, astalgo
import std/intsets
intsets, options, ast, msgs, idents, renderer, types, magicsys,
sempass2, modulegraphs, lineinfos
when defined(nimPreviewSlimSystem):
import std/assertions
import std/[tables]
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
var dest = skipTypes(d, abstractPtrs)
@@ -47,8 +44,6 @@ proc getDispatcher*(s: PSym): PSym =
if dispatcherPos < s.ast.len:
result = s.ast[dispatcherPos].sym
doAssert sfDispatcher in result.flags
else:
result = nil
proc methodCall*(n: PNode; conf: ConfigRef): PNode =
result = n
@@ -67,25 +62,22 @@ type
MethodResult = enum No, Invalid, Yes
proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
result = No
if a.name.id != b.name.id: return
if a.typ.signatureLen != b.typ.signatureLen:
if a.typ.len != b.typ.len:
return
var i = 0
for x, y in paramTypePairs(a.typ, b.typ):
inc i
var aa = x
var bb = y
for i in 1..<a.typ.len:
var aa = a.typ[i]
var bb = b.typ[i]
while true:
aa = skipTypes(aa, {tyGenericInst, tyAlias})
bb = skipTypes(bb, {tyGenericInst, tyAlias})
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
aa = aa.elementType
bb = bb.elementType
aa = aa.lastSon
bb = bb.lastSon
else:
break
if sameType(x, y):
if sameType(a.typ[i], b.typ[i]):
if aa.kind == tyObject and result != Invalid:
result = Yes
elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
@@ -103,11 +95,10 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
return No
if result == Yes:
# check for return type:
# ignore flags of return types; # bug #22673
if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
if not sameTypeOrNil(a.typ[0], b.typ[0]):
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
# infer 'auto' from the base to make it consistent:
b.typ.setReturnType a.typ.returnType
b.typ[0] = a.typ[0]
else:
return No
@@ -122,21 +113,21 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
s.ast[dispatcherPos] = dispatcher
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
var disp = copySym(s, idgen)
incl(disp, sfDispatcher)
excl(disp, sfExported)
var disp = copySym(s, nextSymId(idgen))
incl(disp.flags, sfDispatcher)
excl(disp.flags, sfExported)
let old = disp.typ
disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
copyTypeProps(g, idgen.module, disp.typ, old)
# we can't inline the dispatcher itself (for now):
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast)
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
disp.locImpl.snippet = ""
if s.typ.returnType != nil:
disp.loc.r = ""
if s.typ[0] != nil:
if disp.ast.len > resultPos:
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
else:
# We've encountered a method prototype without a filled-in
# resultPos slot. We put a placeholder in there that will
@@ -158,25 +149,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
var witness: PSym = nil
if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
g.config.isDefined("nimInternalNonVtablesTesting") and sfFromGeneric notin s.flags:
# `sfFromGeneric` excepted: this is the same-module restriction for vtable
# slot placement, and it must be judged on the GENERIC method, not on an
# instance. The generic `method skip[T](x: Input[T])` never reaches here
# (`semMethodPrototype` registers generic methods via `addMethodToGeneric`,
# bypassing `methodDef`); only its instance `skip[string]` does, and that
# instance's first-param type `Input[string]` is owned by whichever module
# first instantiated it (`tparsecombnum`, which `import parsecomb`s and uses
# it), NOT by `Input[T]`'s defining module — so the comparison spuriously
# fails for a method that is perfectly legal at the generic level. (Concrete
# methods, `sfFromGeneric notin flags`, are still checked.)
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
"` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
if sfImportc in s.flags:
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
"` is not allowed to have 'importc' pragmas")
var witness: PSym
for i in 0..<g.methods.len:
let disp = g.methods[i].dispatcher
case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
@@ -190,19 +163,12 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
g.methods[i].methods[0] != s:
# already exists due to forwarding definition?
localError(g.config, s.info, "method is not a base")
logMethodDef(g, s)
return
of No: discard
of Invalid:
if witness.isNil: witness = g.methods[i].methods[0]
# create a new dispatcher:
# stores the id and the position
if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
else:
g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
logMethodDef(g, s)
#echo "adding ", s.info
if witness != nil:
localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
@@ -210,9 +176,8 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
elif sfBase notin s.flags:
message(g.config, s.info, warnUseBase)
proc relevantCol*(methods: seq[PSym], col: int): bool =
proc relevantCol(methods: seq[PSym], col: int): bool =
# returns true iff the position is relevant
result = false
var t = methods[0].typ[col].skipTypes(skipPtrs)
if t.kind == tyObject:
for i in 1..high(methods):
@@ -221,8 +186,7 @@ proc relevantCol*(methods: seq[PSym], col: int): bool =
return true
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
result = 0
for col in FirstParamAt..<a.typ.signatureLen:
for col in 1..<a.typ.len:
if contains(relevantCols, col):
var aa = skipTypes(a.typ[col], skipPtrs)
var bb = skipTypes(b.typ[col], skipPtrs)
@@ -230,7 +194,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
if (d != high(int)) and d != 0:
return d
proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
# we use shellsort here; fast and simple
var n = a.len
var h = 1
@@ -249,16 +213,16 @@ proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
a[j] = v
if h == 1: break
proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
var base = methods[0].ast[dispatcherPos].sym
result = base
var paramLen = base.typ.signatureLen
var paramLen = base.typ.len
var nilchecks = newNodeI(nkStmtList, base.info)
var disp = newNodeI(nkIfStmt, base.info)
var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
let boolType = getSysType(g, unknownLineInfo, tyBool)
for col in FirstParamAt..<paramLen:
for col in 1..<paramLen:
if contains(relevantCols, col):
let param = base.typ.n[col].sym
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
@@ -267,7 +231,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
for meth in 0..high(methods):
var curr = methods[meth] # generate condition:
var cond: PNode = nil
for col in FirstParamAt..<paramLen:
for col in 1..<paramLen:
if contains(relevantCols, col):
var isn = newNodeIT(nkCall, base.info, boolType)
isn.add newSymNode(iss)
@@ -282,7 +246,7 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
cond = a
else:
cond = isn
let retTyp = base.typ.returnType
let retTyp = base.typ[0]
let call = newNodeIT(nkCall, base.info, retTyp)
call.add newSymNode(curr)
for col in 1..<paramLen:
@@ -308,13 +272,14 @@ proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet;
nilchecks.flags.incl nfTransf # should not be further transformed
result.ast[bodyPos] = nilchecks
proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
result = newNode(nkStmtList)
for bucket in 0..<g.methods.len:
var relevantCols = initIntSet()
for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
for col in 1..<g.methods[bucket].methods[0].typ.len:
if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
if optMultiMethods notin g.config.globalOptions:
# if multi-methods are not enabled, we are interested only in the first field
break
sortBucket(g.methods[bucket].methods, relevantCols)
g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))

File diff suppressed because it is too large Load Diff

View File

@@ -7,13 +7,11 @@
# distribution, for details about the copyright.
#
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
import
options, idents, nimconf, extccomp, commands, msgs,
lineinfos, modulegraphs, condsyms, pathutils
import std/[os, parseopt]
lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
when defined(unix):
@@ -53,16 +51,10 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef;
graph: ModuleGraph): bool =
if self.suggestMode:
conf.setCmd cmdCheck
conf.ideActive = true
conf.setCmd cmdIdeTools
if conf.cmd == cmdNimscript:
incl(conf.globalOptions, optWasNimscript)
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
# restores `conf.notes` after loading config files
# because it has overwrites the notes when compiling the system module which
# is a foreign module compared to the project
if conf.cmd in cmdBackends:
conf.notes = conf.mainPackageNotes
if not self.suggestMode:
let scriptFile = conf.projectFull.changeFileExt("nims")

View File

@@ -1,726 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2026 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The "cnif" artifact: the C code generator's output as a NIF file.
##
## This is deliberately *not* NIFC: the C text is kept verbatim (Nim's
## C-level machinery — exception handling in particular — is more refined
## than what NIFC models today; the gap can be closed incrementally later).
## The only structure the artifact adds is the part dead code elimination
## and generic-instance merging need:
##
## - raw C text as string literals
## - every *global* entity's C name as a `Symbol` token
## - every emitted proc definition as a `(cdef SymbolDef flags ...)` group
##
## The C generator marks names with control characters at the single place
## a global's C name is minted (`fillBackendName`) and emits a definition
## directive at the single place finished procs are appended; the marks then
## ride through all of the snippet composition untouched. This module turns
## the final marked module text into the `.c.nif` artifact and strips the
## marks for the actual `.c` output. Rendering C from the artifact is a
## plain token walk: string literals verbatim, symbols by name — which is
## also where a later merge step redirects losing generic instances.
##
## Marker scheme (cannot collide: C string literals escape control chars,
## and `\1`/`\31`/`\23` of cgen's postprocess directives are distinct):
## \2 name \3 a global's C name
## \4 name \31 flags \31 nif \5 start of the definition of `name`;
## `nif` is the defining symbol's NIF name
## (empty for backend-minted symbols) so a
## later run can re-demand the definition
## \4 \5 end of the definitions section
import std / [tables, sets, os, assertions, syncio, algorithm]
import "../dist/nimony/src/lib" / [nifbuilder, nifcoreparse]
const
CnifSymStart* = '\2'
CnifSymEnd* = '\3'
CnifDefStart* = '\4'
CnifDefSep* = '\31' # same separator char as cgen's postprocess directives
CnifDefEnd* = '\5'
proc markCName*(name: string): string {.inline.} =
CnifSymStart & name & CnifSymEnd
proc hasCnifMarks*(s: string): bool =
for c in s:
if c in {CnifSymStart, CnifSymEnd, CnifDefStart}: return true
false
proc stripCnifMarks*(s: string): string =
## Removes the symbol marks (keeping the names) and the definition
## directives (entirely) so the result is plain C.
if not hasCnifMarks(s): return s
result = newStringOfCap(s.len)
var i = 0
while i < s.len:
case s[i]
of CnifSymStart, CnifSymEnd:
inc i
of CnifDefStart:
while i < s.len and s[i] != CnifDefEnd: inc i
inc i # skip CnifDefEnd
else:
result.add s[i]
inc i
const
CnifVersion* = "4"
## Artifact format version, stored in the meta head. Artifacts written
## by an older compiler lack the NIF names and the cref group the
## def-retention check needs (v2), the cdeps group the fine-grained
## reuse gate needs (v3), or the type NIF names and cnif-marked extern
## RTTI references the typeinfo flavor of the def-retention check
## needs (v4); `readCnifHeads` reports them as invalid so their TUs
## simply regenerate once.
proc cnifDefDirective*(name, flags, nifName: string): string =
CnifDefStart & name & CnifDefSep & flags & CnifDefSep & nifName & CnifDefEnd
proc cnifEndDefs*(): string =
CnifDefStart & CnifDefEnd
proc writeCnifArtifact*(code: string; outfile: string;
initRequired = false; datInitRequired = false;
dataDefs: openArray[tuple[cname, nifname: string]] = [];
semmedNif = ""; moduleBase = "";
implDeps: openArray[string] = []) =
## Splits the marked module text into the `.c.nif` artifact.
## The artifact starts with a `(meta <flags> "semmedNif" "moduleBase"
## "version")` head — whether the module has an init/datInit proc
## ('i'/'d'), which semmed NIF it was generated from and the module's
## mangled base name (what `registerModuleToMain` and the reuse decision
## need when the TU is reused in a later run, possibly without the module
## ever being loaded again) — a `(cdata (SymbolDef StrLit)*)` group naming
## the data definitions (consts, globals, RTTI) the TU embeds together
## with their NIF names, a `(cref Ident*)` group naming every C name
## the TU references but does not define itself (what the def-retention
## check consults when some *other* TU regenerates), and a
## `(cdeps Ident*)` group naming the modules whose routine *bodies* this
## TU embeds (redirected defs, shared instances, hooks): the fine-grained
## reuse gate checks their `.impl.nif` cookies on top of the direct
## imports' `.iface.nif` cookies.
# pre-pass: every marked name is a use, every definition directive (and
# every data def) is a definition; external references = uses - defs
var uses = initHashSet[string]()
var defs = initHashSet[string]()
block prePass:
var i = 0
while i < code.len:
case code[i]
of CnifSymStart:
inc i
var name = ""
while i < code.len and code[i] != CnifSymEnd:
name.add code[i]
inc i
inc i
uses.incl name
of CnifDefStart:
inc i
var payload = ""
while i < code.len and code[i] != CnifDefEnd:
payload.add code[i]
inc i
inc i
let sep = find(payload, CnifDefSep)
if sep > 0: defs.incl payload[0..<sep]
elif payload.len > 0: defs.incl payload
else:
inc i
for d in dataDefs: defs.incl d.cname
var crefs: seq[string] = @[]
for u in uses:
if u notin defs: crefs.add u
sort crefs
var b = nifbuilder.open(outfile)
b.withTree "stmts":
b.withTree "meta":
var metaFlags = ""
if initRequired: metaFlags.add 'i'
if datInitRequired: metaFlags.add 'd'
if metaFlags.len > 0: b.addIdent metaFlags
else: b.addEmpty
b.addStrLit semmedNif
b.addStrLit moduleBase
b.addStrLit CnifVersion
b.withTree "cdata":
for d in dataDefs:
b.addSymbolDef d.cname
b.addStrLit d.nifname
b.withTree "cref":
for r in crefs:
b.addIdent r
b.withTree "cdeps":
for s in implDeps:
b.addIdent s
var raw = ""
var inDef = false
template flushRaw() =
if raw.len > 0:
b.addStrLit raw
raw.setLen 0
var i = 0
while i < code.len:
case code[i]
of CnifSymStart:
flushRaw()
inc i
var name = ""
while i < code.len and code[i] != CnifSymEnd:
name.add code[i]
inc i
inc i # skip CnifSymEnd
b.addSymbol name, ""
of CnifDefStart:
flushRaw()
inc i
var payload = ""
while i < code.len and code[i] != CnifDefEnd:
payload.add code[i]
inc i
inc i # skip CnifDefEnd
if inDef:
b.endTree()
inDef = false
if payload.len > 0:
let sep = find(payload, CnifDefSep)
let name = if sep >= 0: payload[0..<sep] else: payload
var flags = if sep >= 0: payload[sep+1..^1] else: ""
var nifName = ""
let sep2 = find(flags, CnifDefSep)
if sep2 >= 0:
nifName = flags[sep2+1..^1]
flags = flags[0..<sep2]
b.addTree "cdef"
b.addSymbolDef name
if flags.len > 0: b.addIdent flags
else: b.addEmpty
b.addStrLit nifName
inDef = true
else:
raw.add code[i]
inc i
flushRaw()
if inDef:
b.endTree()
b.close()
proc renderMarkedC*(code: string; live: HashSet[string]; dropped: var int): string =
## Renders the final C text from the marked module text: symbol marks are
## removed (keeping the names — a later merge step substitutes them here),
## and definitions whose name is not in `live` are dropped entirely. Each
## definition is self-delimiting (genProcAux emits an end directive right
## after the proc's text), so text written by other emitters is never part
## of a definition's span and survives unconditionally.
result = newStringOfCap(code.len)
var i = 0
while i < code.len:
case code[i]
of CnifSymStart, CnifSymEnd:
inc i
of CnifDefStart:
var payload = ""
inc i
while i < code.len and code[i] != CnifDefEnd:
payload.add code[i]
inc i
inc i # skip CnifDefEnd
if payload.len > 0:
let sep = find(payload, CnifDefSep)
let name = if sep >= 0: payload[0..<sep] else: payload
if name notin live:
inc dropped
# drop the definition's text: everything up to its end directive
while i < code.len and code[i] != CnifDefStart: inc i
else:
result.add code[i]
inc i
# ---- Liveness over the artifact -------------------------------------------
proc symOrIdentName(c: Cursor): string {.inline.} =
if c.kind == Ident: strVal(c) else: symName(c)
type
CnifHeads* = object
## The cheap-to-parse part of an artifact that a later run needs in
## order to reuse the TU without regenerating it.
valid*: bool ## file parsed, carries the meta head and has
## the current format version
initRequired*: bool
datInitRequired*: bool
semmedNif*: string ## the semmed NIF this TU was generated from
moduleBase*: string ## the module's mangled base name
cdefs*: seq[tuple[cname, nifname: string]] ## the proc definitions
cdata*: seq[tuple[cname, nifname: string]] ## the data definitions
crefs*: seq[string] ## C names referenced but not defined here
cdeps*: seq[string] ## module suffixes whose routine bodies this
## TU embeds (impl-cookie gated on reuse)
proc readCnifHeads*(f: string): CnifHeads =
## Reads `(meta ...)`, `(cdata ...)`, `(cref ...)` and the `(cdef ...)`
## head names from an artifact. Artifacts written by an older compiler
## (no meta head or a different format version) report `valid=false`.
result = CnifHeads()
if not fileExists(f): return
var pool = newPool()
var tags = newTagPool()
let stmtsTag = tags.registerTag("stmts")
let cdefTag = tags.registerTag("cdef")
let cdataTag = tags.registerTag("cdata")
let crefTag = tags.registerTag("cref")
let cdepsTag = tags.registerTag("cdeps")
let metaTag = tags.registerTag("meta")
var buf = parseFromFile(f, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != stmtsTag:
endRead(c)
return
var version = ""
var sawMeta = false
c.loopInto:
if c.kind == TagLit:
if c.cursorTagId == metaTag:
sawMeta = true
var strIdx = 0
c.loopInto:
if c.kind == Ident:
for ch in strVal(c):
if ch == 'i': result.initRequired = true
elif ch == 'd': result.datInitRequired = true
inc c
elif c.kind == StrLit:
if strIdx == 0: result.semmedNif = strVal(c)
elif strIdx == 1: result.moduleBase = strVal(c)
elif strIdx == 2: version = strVal(c)
inc strIdx
inc c
else:
skip c
elif c.cursorTagId == cdataTag:
c.loopInto:
if c.kind == SymbolDef:
result.cdata.add (symName(c), "")
inc c
elif c.kind == StrLit:
if result.cdata.len > 0:
result.cdata[^1].nifname = strVal(c)
inc c
else:
skip c
elif c.cursorTagId == crefTag:
c.loopInto:
if c.kind in {Ident, Symbol, SymbolDef}:
result.crefs.add symOrIdentName(c)
inc c
else:
skip c
elif c.cursorTagId == cdepsTag:
c.loopInto:
if c.kind in {Ident, Symbol, SymbolDef}:
result.cdeps.add symOrIdentName(c)
inc c
else:
skip c
elif c.cursorTagId == cdefTag:
# fixed head: SymbolDef, flags (Ident or empty), NIF name StrLit;
# everything after that is the definition's body text
var state = 0
c.loopInto:
if c.kind == SymbolDef:
result.cdefs.add (symName(c), "")
state = 1
inc c
elif state == 1: # the flags field
state = 2
skip c
elif state == 2: # the NIF name
if c.kind == StrLit and result.cdefs.len > 0:
result.cdefs[^1].nifname = strVal(c)
state = 3
skip c
else:
skip c
else:
skip c
else:
skip c
endRead(c)
result.valid = sawMeta and version == CnifVersion
type
CnifLiveness* = object
defs*: int ## proc definitions emitted across all modules
liveDefs*: int ## of those, reachable from the roots
live*: HashSet[string] ## live C names
broken*: bool
proc computeLiveFromCArtifacts*(files: openArray[string]): CnifLiveness =
## dce1-style mark&sweep over the C-shaped artifacts: a `(cdef ...)`
## group is a definition (flags 'x'/'c'/'m' — exportc, compilerproc,
## method/dispatcher — make it a root), names at the top level (data,
## globals, init code) are roots, names inside a group are its uses.
## Because the artifact is *fully lowered* output, no conservative
## modelling is needed: every call the C code contains is a token here.
##
## NB: mangled C names contain no dots, so NIF's text reader classifies
## them as `Ident` rather than `Symbol`; the dialect therefore treats
## Ident tokens as name uses. Inside a `(cdef ...)` the flags ident is
## the one immediately following the SymbolDef; everything after is a use.
result = CnifLiveness(live: initHashSet[string]())
var pool = newPool()
var tags = newTagPool()
let stmtsTag = tags.registerTag("stmts")
let cdefTag = tags.registerTag("cdef")
let cdataTag = tags.registerTag("cdata")
let crefTag = tags.registerTag("cref")
let cdepsTag = tags.registerTag("cdeps")
let metaTag = tags.registerTag("meta")
var uses = initTable[string, HashSet[string]]()
var roots = initHashSet[string]()
var defs = initHashSet[string]()
for f in files:
if not fileExists(f):
result.broken = true
return
var buf = parseFromFile(f, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != stmtsTag:
result.broken = true
endRead(c)
return
c.loopInto:
case c.kind
of Symbol, Ident:
roots.incl symOrIdentName(c)
inc c
of TagLit:
if c.cursorTagId == metaTag or c.cursorTagId == cdataTag or
c.cursorTagId == crefTag or c.cursorTagId == cdepsTag:
# bookkeeping for TU reuse, irrelevant for liveness
skip c
elif c.cursorTagId == cdefTag:
var owner = ""
var flagsSeen = false
c.loopInto:
case c.kind
of SymbolDef:
owner = symName(c)
defs.incl owner
flagsSeen = false
inc c
of Symbol, Ident:
let name = symOrIdentName(c)
if not flagsSeen:
# the flags field right after the SymbolDef
flagsSeen = true
for ch in name:
# 'd' marks a data definition (const/RTTI): never DCE'd, so it
# is a root whose body keeps its referenced procs live
if ch in {'x', 'c', 'm', 'd'}:
roots.incl owner
break
else:
uses.mgetOrPut(owner, initHashSet[string]()).incl name
inc c
of DotToken:
flagsSeen = true # empty flags field
inc c
else:
skip c
else:
c.loopInto:
if c.kind in {Symbol, Ident}:
roots.incl symOrIdentName(c)
inc c
else:
skip c
else:
skip c
endRead(c)
# mark & sweep
var work = newSeqOfCap[string](roots.len)
for r in roots: work.add r
while work.len > 0:
let s = work.pop()
if not result.live.containsOrIncl(s):
if uses.hasKey(s):
for dep in uses[s]:
if dep notin result.live:
work.add dep
result.defs = defs.len
for d in defs:
if d in result.live: inc result.liveDefs
# ---- The merge stage: liveness + owner assignment -------------------------
type
MergeDecision* = object
## What the per-module backend's `merge` stage computes from every
## module's `.c.nif` and what its `emit` stage consumes to render the
## final `.c` of one module.
live*: HashSet[string] ## globally reachable C names (dead cdefs
## are dropped from every module)
owners*: Table[string, string] ## for each `'u'`-flagged (unique,
## externally-linked) definition, the single
## artifact base name allowed to embed its
## body; every other module prototypes it
broken*: bool ## an artifact was missing or unparsable —
## the caller should fall back / regenerate
defs*, liveDefs*: int
proc computeMergeDecision*(files: openArray[string]): MergeDecision =
## One pass over every `.c.nif`: the same mark&sweep as
## `computeLiveFromCArtifacts` plus, per definition, owner assignment.
##
## Each `cg` process emits the body of every definition it demands
## (emit-everywhere), so the same externally-linked definition appears in
## several artifacts. A `'u'` flag on the `(cdef ...)` marks those that need
## exactly one owner, assigned here across processes: the owner is the
## lexicographically smallest artifact that emits it — a pure function of the
## claimant set, hence stable across rebuilds. Definitions without `'u'`
## (inline procs, dispatchers) are `static`/main-only and emitted into every
## using TU, so they get no owner entry and are never deduplicated.
result = MergeDecision(live: initHashSet[string](),
owners: initTable[string, string]())
var pool = newPool()
var tags = newTagPool()
let stmtsTag = tags.registerTag("stmts")
let cdefTag = tags.registerTag("cdef")
let cdataTag = tags.registerTag("cdata")
let crefTag = tags.registerTag("cref")
let cdepsTag = tags.registerTag("cdeps")
let metaTag = tags.registerTag("meta")
var uses = initTable[string, HashSet[string]]()
var roots = initHashSet[string]()
var defs = initHashSet[string]()
for f in files:
if not fileExists(f):
result.broken = true
return
let owner = extractFilename(f)
var buf = parseFromFile(f, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != stmtsTag:
result.broken = true
endRead(c)
return
c.loopInto:
case c.kind
of Symbol, Ident:
roots.incl symOrIdentName(c)
inc c
of TagLit:
if c.cursorTagId == metaTag or c.cursorTagId == cdataTag or
c.cursorTagId == crefTag or c.cursorTagId == cdepsTag:
skip c
elif c.cursorTagId == cdefTag:
var ownerName = ""
var flagsSeen = false
var needsOwner = false
c.loopInto:
case c.kind
of SymbolDef:
ownerName = symName(c)
defs.incl ownerName
flagsSeen = false
inc c
of Symbol, Ident:
let name = symOrIdentName(c)
if not flagsSeen:
flagsSeen = true
for ch in name:
if ch in {'x', 'c', 'm'}: roots.incl ownerName
# 'u' = unique proc (DCE'd), 'd' = data (never DCE'd, hence a
# root); both need a single owner across the emit-everywhere
# processes
elif ch == 'u': needsOwner = true
elif ch == 'd':
needsOwner = true
roots.incl ownerName
else:
uses.mgetOrPut(ownerName, initHashSet[string]()).incl name
inc c
of DotToken:
flagsSeen = true # empty flags field
inc c
else:
skip c
if needsOwner and ownerName.len > 0:
# smallest claimant wins; ties impossible (one entry per name)
let prev = result.owners.getOrDefault(ownerName, "")
if prev.len == 0 or owner < prev:
result.owners[ownerName] = owner
else:
c.loopInto:
if c.kind in {Symbol, Ident}:
roots.incl symOrIdentName(c)
inc c
else:
skip c
else:
skip c
endRead(c)
var work = newSeqOfCap[string](roots.len)
for r in roots: work.add r
while work.len > 0:
let s = work.pop()
if not result.live.containsOrIncl(s):
if uses.hasKey(s):
for dep in uses[s]:
if dep notin result.live:
work.add dep
result.defs = defs.len
for d in defs:
if d in result.live: inc result.liveDefs
const MergeDecisionFile* = "ic.backend.merge.nif"
## Fixed name of the merge stage's output in the nimcache, read by `emit`.
proc writeMergeDecision*(outfile: string; d: MergeDecision) =
## Serializes the merge decision: `(merge (live Symbol*) (owners (own
## Symbol StrLit)*))`. C names are mangled (no dots) so they serialize as
## symbols; owner artifact base names go in string literals.
var live: seq[string] = @[]
for n in d.live: live.add n
sort live
var keys: seq[string] = @[]
for k in d.owners.keys: keys.add k
sort keys
var b = nifbuilder.open(outfile)
b.withTree "merge":
b.withTree "live":
for n in live: b.addSymbol n, ""
b.withTree "owners":
for k in keys:
b.withTree "own":
b.addSymbol k, ""
b.addStrLit d.owners[k]
b.close()
proc readMergeDecision*(f: string): MergeDecision =
## Reads back a `writeMergeDecision` file; `broken=true` if absent/unparsable.
result = MergeDecision(live: initHashSet[string](),
owners: initTable[string, string]())
if not fileExists(f):
result.broken = true
return
var pool = newPool()
var tags = newTagPool()
let mergeTag = tags.registerTag("merge")
let liveTag = tags.registerTag("live")
let ownersTag = tags.registerTag("owners")
let ownTag = tags.registerTag("own")
var buf = parseFromFile(f, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != mergeTag:
result.broken = true
endRead(c)
return
c.loopInto:
if c.kind == TagLit and c.cursorTagId == liveTag:
c.loopInto:
if c.kind in {Symbol, Ident}:
result.live.incl symOrIdentName(c)
inc c
else:
skip c
elif c.kind == TagLit and c.cursorTagId == ownersTag:
c.loopInto:
if c.kind == TagLit and c.cursorTagId == ownTag:
var key = ""
c.loopInto:
if c.kind in {Symbol, Ident}:
key = symOrIdentName(c)
inc c
elif c.kind == StrLit:
if key.len > 0: result.owners[key] = strVal(c)
inc c
else:
skip c
else:
skip c
else:
skip c
endRead(c)
proc renderCFromArtifact*(artifact: string; d: MergeDecision; ownerId: string;
dropped: var int): string =
## The per-module backend's `emit` stage: render one module's final `.c` from
## its `.c.nif` and the merge decision. String literals are emitted verbatim,
## symbols by name; a `(cdef ...)` body is dropped when the name is dead, or
## when it is a `'u'` unique definition this module does not own. The body's
## prototype lives in the surrounding raw text (cgen emits a forward
## declaration for every *used* proc, independent of where the body lands), so
## a dropped body still leaves a valid declaration — no synthesis needed. The
## head groups (meta/cdata/cref/cdeps) carry no C text.
result = ""
if not fileExists(artifact): return
var pool = newPool()
var tags = newTagPool()
let stmtsTag = tags.registerTag("stmts")
let cdefTag = tags.registerTag("cdef")
var buf = parseFromFile(artifact, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != stmtsTag:
endRead(c)
return
c.loopInto:
case c.kind
of StrLit:
result.add strVal(c)
inc c
of Symbol, Ident:
result.add symOrIdentName(c)
inc c
of TagLit:
if c.cursorTagId == cdefTag:
# fixed head: SymbolDef, flags (Ident or empty), nifname StrLit; the
# rest is the definition's body text. `state` counts past the head.
var name = ""
var isUnique = false
var isData = false
var keep = true
var state = 0
c.loopInto:
if state == 0 and c.kind == SymbolDef:
name = symName(c)
state = 1
inc c
elif state == 1: # the flags field (one token: Ident/Symbol or empty)
if c.kind in {Ident, Symbol}:
for ch in symOrIdentName(c):
if ch == 'u': isUnique = true
elif ch == 'd': isData = true
state = 2
inc c
elif state == 2: # the NIF name (one StrLit) — decide keep here
let owned = d.owners.getOrDefault(name, ownerId) == ownerId
keep =
if isData: owned # data: kept by its owner only
elif isUnique: (name in d.live) and owned
else: name in d.live # inline/dispatcher: per-TU
if not keep: inc dropped
state = 3
inc c
else: # body tokens
if keep:
if c.kind == StrLit: result.add strVal(c)
elif c.kind in {Symbol, Ident}: result.add symOrIdentName(c)
inc c
else:
# head groups (meta/cdata/cref/cdeps) carry no C text
skip c
else:
inc c
endRead(c)

View File

@@ -24,12 +24,10 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
import std/[setutils, sets, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
import
msgs, options, nversion, condsyms, extccomp, platform,
wordrecg, nimblecmd, lineinfos, pathutils
import std/pathnorm
wordrecg, nimblecmd, lineinfos, pathutils, pathnorm
from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
@@ -118,7 +116,7 @@ const
errInvalidCmdLineOption = "invalid command line option: '$1'"
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
errOffHintsError = "'off', 'hint', 'warning', 'error' or 'usages' expected, but '$1' found"
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
@@ -186,7 +184,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
info: TLineInfo; orig: string; conf: ConfigRef) =
var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off
var i = 0
var notes: set[TMsgKind] = {}
var notes: set[TMsgKind]
var isBracket = false
if i < arg.len and arg[i] == '[':
isBracket = true
@@ -203,16 +201,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
else: invalidCmdLineOption(conf, pass, orig, info)
let isSomeHint = state in {wHint, wHintAsError}
let isSomeWarning = state in {wWarning, wWarningAsError}
template findNote(noteMin, noteMax, name) =
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
let x = findStr(noteMin, noteMax, id, errUnknown)
if x != errUnknown: notes = {TNoteKind(x)}
else:
if isSomeHint or isSomeWarning:
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
else:
localError(conf, info, "unknown $#: $#" % [name, id])
else: localError(conf, info, "unknown $#: $#" % [name, id])
case id.normalize
of "all": # other note groups would be easy to support via additional cases
notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
@@ -245,12 +238,10 @@ proc processCompile(conf: ConfigRef; filename: string) =
extccomp.addExternalFileToCompile(conf, found)
const
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'yrc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
errDefaultOrSsoExpectedButXFound = "'default' or 'sso' expected, but '$1' found"
template warningOptionNoop(switch: string) =
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -267,23 +258,17 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
of "destructors", "arc": result = conf.selectedGC == gcArc
of "orc": result = conf.selectedGC == gcOrc
of "yrc": result = conf.selectedGC == gcYrc
of "hooks": result = conf.selectedGC == gcHooks
of "go": result = conf.selectedGC == gcGo
of "none": result = conf.selectedGC == gcNone
of "stack", "regions": result = conf.selectedGC == gcRegions
of "atomicarc": result = conf.selectedGC == gcAtomicArc
else:
result = false
localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
of "opt":
case arg.normalize
of "speed": result = contains(conf.options, optOptimizeSpeed)
of "size": result = contains(conf.options, optOptimizeSize)
of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {}
else:
result = false
localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
of "verbosity": result = $conf.verbosity == arg
of "app":
case arg.normalize
@@ -293,9 +278,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
not contains(conf.globalOptions, optGenGuiApp)
of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
not contains(conf.globalOptions, optGenGuiApp)
else:
result = false
localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
of "dynliboverride":
result = isDynlibOverride(conf, arg)
of "exceptions":
@@ -304,25 +287,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "setjmp": result = conf.exc == excSetjmp
of "quirky": result = conf.exc == excQuirky
of "goto": result = conf.exc == excGoto
else:
result = false
localError(conf, info, errInvalidExceptionSystem % arg)
of "strings":
case arg.normalize
of "default": result = conf.selectedStrings == stringDefault
of "sso": result = conf.selectedStrings == stringSso
else:
result = false
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
of "experimental":
try:
result = conf.features.contains parseEnum[Feature](arg)
except ValueError:
result = false
localError(conf, info, errInvalidFeatureButXFound % arg)
else:
result = false
invalidCmdLineOption(conf, passCmd1, switch, info)
else: localError(conf, info, errInvalidExceptionSystem % arg)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
case switch.normalize
@@ -368,15 +334,9 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
result = contains(conf.options, optTrMacros)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs", "nilchecks", "taintmode":
warningOptionNoop(switch)
result = false
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
of "panics": result = contains(conf.globalOptions, optPanics)
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
of "mangle": result = contains(conf.globalOptions, optItaniumMangle)
else:
result = false
invalidCmdLineOption(conf, passCmd1, switch, info)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
notRelativeToProj = false): AbsoluteDir =
@@ -418,8 +378,7 @@ proc makeAbsolute(s: string): AbsoluteFile =
proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
info: TLineInfo) =
## set tracking info, common code for track, trackDirty, & ideTrack
var ln: int = 0
var col: int = 0
var ln, col: int
if parseUtils.parseInt(line, ln) <= 0:
localError(conf, info, errInvalidNumber % line)
if parseUtils.parseInt(column, col) <= 0:
@@ -469,7 +428,7 @@ template handleStdinOrCmdInput =
conf.outDir = getNimcacheDir(conf)
proc handleStdinInput*(conf: ConfigRef) =
conf.projectName = conf.stdinFile.string
conf.projectName = "stdinfile"
conf.projectIsStdin = true
handleStdinOrCmdInput()
@@ -483,9 +442,7 @@ proc parseCommand*(command: string): Command =
of "cpp", "compiletocpp": cmdCompileToCpp
of "objc", "compiletooc": cmdCompileToOC
of "js", "compiletojs": cmdCompileToJS
of "nif": cmdCompileToNif
of "r": cmdCrun
of "m": cmdM
of "run": cmdTcc
of "check": cmdCheck
of "e": cmdNimscript
@@ -503,12 +460,10 @@ proc parseCommand*(command: string): Command =
of "gendepend": cmdGendepend
of "dump": cmdDump
of "parse": cmdParse
of "rod": cmdRod
of "secret": cmdInteractive
of "nop", "help": cmdNop
of "jsonscript": cmdJsonscript
of "nifc": cmdNifC # generate C from NIF files
of "ic": cmdIc # generate .build.nif for nifmake
of "icconfig": cmdIcConfig # produce the precompiled config artifact
else: cmdUnknown
proc setCmd*(conf: ConfigRef, cmd: Command) =
@@ -520,12 +475,6 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
of cmdCompileToCpp: conf.backend = backendCpp
of cmdCompileToOC: conf.backend = backendObjc
of cmdCompileToJS: conf.backend = backendJs
of cmdCompileToNif: conf.backend = backendNif
of cmdNifC:
conf.backend = backendC # NIF to C compilation
of cmdM:
# cmdM requires optCompress for proper IC handling (include files, etc.)
conf.globalOptions.incl optCompress
else: discard
proc setCommandEarly*(conf: ConfigRef, command: string) =
@@ -566,7 +515,14 @@ proc initOrcDefines*(conf: ConfigRef) =
if conf.exc == excNone and conf.backend != backendCpp:
conf.exc = excGoto
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
if isOrc:
conf.selectedGC = gcOrc
defineSymbol(conf.symbols, "gcorc")
else:
conf.selectedGC = gcArc
defineSymbol(conf.symbols, "gcarc")
defineSymbol(conf.symbols, "gcdestructors")
incl conf.globalOptions, optSeqDestructors
incl conf.globalOptions, optTinyRtti
@@ -576,12 +532,10 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
if conf.exc == excNone and conf.backend != backendCpp:
conf.exc = excGoto
proc unregisterArcOrc*(conf: ConfigRef) =
proc unregisterArcOrc(conf: ConfigRef) =
undefSymbol(conf.symbols, "gcdestructors")
undefSymbol(conf.symbols, "gcarc")
undefSymbol(conf.symbols, "gcorc")
undefSymbol(conf.symbols, "gcyrc")
undefSymbol(conf.symbols, "gcatomicarc")
undefSymbol(conf.symbols, "nimSeqsV2")
undefSymbol(conf.symbols, "nimV2")
excl conf.globalOptions, optSeqDestructors
@@ -607,21 +561,9 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
conf.selectedGC = gcMarkAndSweep
defineSymbol(conf.symbols, "gcmarkandsweep")
of "destructors", "arc":
conf.selectedGC = gcArc
defineSymbol(conf.symbols, "gcarc")
registerArcOrc(pass, conf)
registerArcOrc(pass, conf, false)
of "orc":
conf.selectedGC = gcOrc
defineSymbol(conf.symbols, "gcorc")
registerArcOrc(pass, conf)
of "yrc":
conf.selectedGC = gcYrc
defineSymbol(conf.symbols, "gcyrc")
registerArcOrc(pass, conf)
of "atomicarc":
conf.selectedGC = gcAtomicArc
defineSymbol(conf.symbols, "gcatomicarc")
registerArcOrc(pass, conf)
registerArcOrc(pass, conf, true)
of "hooks":
conf.selectedGC = gcHooks
defineSymbol(conf.symbols, "gchooks")
@@ -643,29 +585,10 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
defineSymbol(conf.symbols, "gcregions")
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): string =
if pass == passPP and not isAbsolute(arg):
assert isAbsolute(conf.currentConfigDir), "something is wrong with currentConfigDir"
result = conf.currentConfigDir / arg
else:
result = arg
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf: ConfigRef) =
var key = ""
var val = ""
# Record config-file switches so the `nim ic` driver can serialise them into a
# precompiled-config artifact and have its per-module child processes replay
# them instead of re-parsing the `nim.cfg` chain (and re-running `config.nims`
# in the VM) on every invocation. Only `passPP` (config-file) switches are
# captured; command-line switches are forwarded by the build graph as usual.
# Path-search switches are skipped: their net effect already lives in the
# resolved `searchPaths` the driver forwards as `--path`, and replaying their
# raw (often relative-to-config-dir) arguments here would misresolve.
if pass == passPP and switch.normalize notin
["path", "p", "nimblepath", "lazypath", "excludepath",
"nonimblepath", "clearnimblepath", "nimcache"]:
conf.icConfigSwitches.add (switch, arg)
var
key, val: string
case switch.normalize
of "eval":
expectArg(conf, switch, arg, pass, info)
@@ -680,17 +603,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
for path in nimbleSubs(conf, arg):
addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
else: processPath(conf, path, info), info)
of "nimblepath":
of "nimblepath", "babelpath":
if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath")
if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
expectArg(conf, switch, arg, pass, info)
var path = processPath(conf, arg, info, notRelativeToProj=true)
let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
if not nimbleDir.isEmpty and pass == passPP:
path = nimbleDir / RelativeDir"pkgs2"
nimblePath(conf, path, info)
path = nimbleDir / RelativeDir"pkgs"
nimblePath(conf, path, info)
of "nonimblepath":
of "nonimblepath", "nobabelpath":
if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath")
expectNoArg(conf, switch, arg, pass, info)
disableNimblePath(conf)
of "clearnimblepath":
@@ -707,7 +630,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
# refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set
# in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")`
if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/')
conf.nimcacheDir = processPath(conf, pathRelativeToConfig(arg, pass, conf), info, notRelativeToProj=true)
conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true)
of "out", "o":
expectArg(conf, switch, arg, pass, info)
let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string)
@@ -718,14 +641,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf.outDir = processPath(conf, arg, info, notRelativeToProj=true)
of "usenimcache":
processOnOffSwitchG(conf, {optUseNimcache}, arg, pass, info)
of "ideimports":
# nimsuggest: where the import closure comes from. IC is opt-in.
# nif|on load unchanged imports from precompiled NIF (cmdM)
# source|off (default) recompile the whole closure from source (cmdCheck)
case arg.normalize
of "nif", "on", "": conf.ideImportsFromNif = true
of "source", "off": conf.ideImportsFromNif = false
else: localError(conf, info, "'--ideImports' expects 'nif' or 'source', got: '$1'" % arg)
of "docseesrcurl":
expectArg(conf, switch, arg, pass, info)
conf.docSeeSrcUrl = arg
@@ -734,11 +649,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "backend", "b":
let backend = parseEnum(arg.normalize, TBackend.default)
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
if backend == backendJs: # bug #21209
conf.globalOptions.excl {optThreadAnalysis, optThreads}
if optRun in conf.globalOptions:
# for now, -r uses nodejs, so define nodejs
defineSymbol(conf.symbols, "nodejs")
conf.backend = backend
of "doccmd": conf.docCmd = arg
of "define", "d":
@@ -779,17 +689,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processMemoryManagementOption(switch, arg, pass, info, conf)
of "mm":
processMemoryManagementOption(switch, arg, pass, info, conf)
of "strings":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
case arg.normalize
of "default":
conf.selectedStrings = stringDefault
of "sso":
conf.selectedStrings = stringSso
defineSymbol(conf.symbols, "nimsso")
else:
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
of "warnings", "w":
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
@@ -815,16 +714,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf.globalOptions.excl optCDebug
else:
localError(conf, info, "expected native|gdb|on|off but found " & arg)
of "mangle":
case arg.normalize
of "nim":
conf.globalOptions.excl optItaniumMangle
of "cpp":
conf.globalOptions.incl optItaniumMangle
else:
localError(conf, info, "expected nim|cpp but found " & arg)
of "compress":
conf.globalOptions.incl optCompress
of "g": # alias for --debugger:native
conf.globalOptions.incl optCDebug
conf.options.incl optLineDir
@@ -870,10 +759,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
of "tlsemulation":
processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
if optTlsEmulation in conf.globalOptions:
conf.legacyFeatures.incl emitGenerics
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
of "implicitstatic":
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
of "patterns", "trmacros":
@@ -910,7 +796,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
defineSymbol(conf.symbols, "dll")
of "staticlib":
incl(conf.globalOptions, optGenStaticLib)
incl(conf.globalOptions, optNoMain)
excl(conf.globalOptions, optGenGuiApp)
defineSymbol(conf.symbols, "library")
defineSymbol(conf.symbols, "staticlib")
@@ -934,75 +819,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "header":
if conf != nil: conf.headerFile = arg
incl(conf.globalOptions, optGenIndex)
of "nimbasepattern":
if conf != nil: conf.nimbasePattern = arg
of "index":
case arg.normalize
of "", "on": conf.globalOptions.incl {optGenIndex}
of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex}
of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly}
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
of "noimportdoc":
processOnOffSwitchG(conf, {optNoImportdoc}, arg, pass, info)
of "ismainmodule":
# `nim m` (IC) only: marks the single module being checked as the program's
# real entry point so that `isMainModule` and `when isMainModule:` resolve
# correctly even though every module is compiled with `sfMainModule` set.
conf.isMainModule = switchOn(arg)
of "icgroup":
# `nim m` only: register a module that belongs to the current strongly-
# connected import group, so it is compiled from source (not loaded from a
# precompiled NIF) and gets its own NIF written. `deps.nim` emits one
# `--icGroup:<path>` per member of a dependency cycle. The argument is an
# absolute .nim path produced by the dependency scanner.
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
conf.icGroup.incl(canonicalizePath(conf, AbsoluteFile arg).string)
of "icproject":
# `nim m`/`nim nifc` only: the ORIGINAL project file (see options.icProject)
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
conf.icProject = canonicalizePath(conf, AbsoluteFile arg).string
of "icpreparsedconfig":
# `nim m`/`nim nifc` only: path of the precompiled-config artifact (see
# options.icPreparsedConfig). Read in `passCmd1`, before `loadConfigs`, so
# config loading can replay it instead of re-parsing the `nim.cfg` chain.
expectArg(conf, switch, arg, pass, info)
conf.icPreparsedConfig = arg
of "icconfigout":
# `nim icconfig` only: where to write the precompiled config artifact (see
# options.icConfigOut). The `nim ic` driver spawns the producer with this.
expectArg(conf, switch, arg, pass, info)
conf.icConfigOut = arg
of "icbackendstage":
# `nim nifc` only: per-module backend stage, one of cg|merge|emit (see
# options.icBackendStage). Empty (switch unused) keeps the whole-program
# backend. Emitted by `deps.nim`'s backend build file.
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
conf.icBackendStage = arg
of "icbackendmodule":
# `nim nifc` only: the NIF module suffix the cg/emit stage operates on (see
# options.icBackendModule).
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
conf.icBackendModule = arg
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
of "import":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
let m = findModule(conf, arg, toFullPath(conf, info)).string
if m.len == 0:
localError(conf, info, "Cannot resolve filename: " & arg)
else:
conf.implicitImports.add(if arg.startsWith(stdPrefix): arg else: m)
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
of "include":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
let m = findModule(conf, arg, toFullPath(conf, info)).string
if m.len == 0:
localError(conf, info, "Cannot resolve filename: " & arg)
else:
conf.implicitIncludes.add m
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
of "listcmd":
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
of "asm":
@@ -1027,38 +853,21 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
setTarget(conf.target, conf.target.targetOS, cpu)
of "run", "r":
processOnOffSwitchG(conf, {optRun}, arg, pass, info)
if conf.backend == backendJs:
# for now, -r uses nodejs, so define nodejs
defineSymbol(conf.symbols, "nodejs")
of "maxloopiterationsvm":
expectArg(conf, switch, arg, pass, info)
var value: int = 10_000_000
discard parseSaturatedNatural(arg, value)
if value <= 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
conf.maxLoopIterationsVM = value
of "maxcalldepthvm":
expectArg(conf, switch, arg, pass, info)
var value: int = 2_000
discard parseSaturatedNatural(arg, value)
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
conf.maxCallDepthVM = value
conf.maxLoopIterationsVM = parseInt(arg)
of "errormax":
expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this.
# `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
# If user doesn't set this flag and the code doesn't either, it'd
# have the same effect as errorMax = 1
var value: int = 0
discard parseSaturatedNatural(arg, value)
conf.errorMax = if value == 0: high(int) else: value
of "stdinfile":
expectArg(conf, switch, arg, pass, info)
conf.stdinFile = if os.isAbsolute(arg): AbsoluteFile(arg)
else: AbsoluteFile(getCurrentDir() / arg)
let ret = parseInt(arg)
conf.errorMax = if ret == 0: high(int) else: ret
of "verbosity":
expectArg(conf, switch, arg, pass, info)
let verbosity = parseInt(arg)
if verbosity notin 0..3:
if verbosity notin {0..3}:
localError(conf, info, "invalid verbosity level: '$1'" % arg)
conf.verbosity = verbosity
var verb = NotesVerbosity[conf.verbosity]
@@ -1068,9 +877,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
conf.mainPackageNotes = conf.notes
of "parallelbuild":
expectArg(conf, switch, arg, pass, info)
var value: int = 0
discard parseSaturatedNatural(arg, value)
conf.numberOfProcessors = value
conf.numberOfProcessors = parseInt(arg)
of "version", "v":
expectNoArg(conf, switch, arg, pass, info)
writeVersionInfo(conf, pass)
@@ -1088,8 +895,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
# xxx maybe also ic, since not in help?
if pass in {passCmd2, passPP}:
case arg.normalize
of "on": conf.ic = true
of "legacy": conf.symbolFiles = v2Sf
of "on": conf.symbolFiles = v2Sf
of "off": conf.symbolFiles = disabledSf
of "writeonly": conf.symbolFiles = writeOnlySf
of "readonly": conf.symbolFiles = readOnlySf
@@ -1194,12 +1000,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectNoArg(conf, switch, arg, pass, info)
conf.exc = low(ExceptionSystem)
defineSymbol(conf.symbols, "noCppExceptions")
of "shownonexports":
expectNoArg(conf, switch, arg, pass, info)
showNonExportedFields(conf)
of "raw":
expectNoArg(conf, switch, arg, pass, info)
docRawOutput(conf)
of "exceptions":
case arg.normalize
of "cpp": conf.exc = excCpp
@@ -1231,10 +1031,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
defineSymbol(conf.symbols, "nimSeqsV2")
of "stylecheck":
case arg.normalize
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError, optStyleWarning}
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError, optStyleWarning}
of "warning": conf.globalOptions = conf.globalOptions + {optStyleWarning} - {optStyleHint, optStyleError}
of "error": conf.globalOptions = conf.globalOptions + {optStyleError} - {optStyleHint, optStyleWarning}
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
of "usages": conf.globalOptions.incl optStyleUsages
else: localError(conf, info, errOffHintsError % arg)
of "showallmismatches":
@@ -1255,6 +1054,29 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "expandarc":
expectArg(conf, switch, arg, pass, info)
conf.arcToExpand[arg] = "T"
of "useversion":
expectArg(conf, switch, arg, pass, info)
case arg
of "1.0":
defineSymbol(conf.symbols, "NimMajor", "1")
defineSymbol(conf.symbols, "NimMinor", "0")
# old behaviors go here:
defineSymbol(conf.symbols, "nimOldRelativePathBehavior")
undefSymbol(conf.symbols, "nimDoesntTrackDefects")
ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect}
conf.globalOptions.incl optNimV1Emulation
of "1.2":
defineSymbol(conf.symbols, "NimMajor", "1")
defineSymbol(conf.symbols, "NimMinor", "2")
conf.globalOptions.incl optNimV12Emulation
of "1.6":
defineSymbol(conf.symbols, "NimMajor", "1")
defineSymbol(conf.symbols, "NimMinor", "6")
conf.globalOptions.incl optNimV16Emulation
else:
localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
# always be compatible with 1.x.100:
defineSymbol(conf.symbols, "NimPatch", "100")
of "benchmarkvm":
processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
of "profilevm":
@@ -1268,8 +1090,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
if optPanics in conf.globalOptions:
defineSymbol(conf.symbols, "nimPanics")
of "jsbigint64":
processOnOffSwitchG(conf, {optJsBigInt64}, arg, pass, info)
of "sourcemap": # xxx document in --fullhelp
conf.globalOptions.incl optSourcemap
conf.options.incl optLineDir
@@ -1284,8 +1104,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else: invalidCmdLineOption(conf, pass, switch, info)
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
var cmd = ""
var arg = ""
var cmd, arg: string
splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
processSwitch(cmd, arg, pass, gCmdLineInfo, config)
@@ -1312,10 +1131,7 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
config.projectName = unixToNativePath(p.key)
config.arguments = cmdLineRest(p)
result = true
elif pass != passCmd2:
setCommandEarly(config, p.key)
result = false
else: result = false
elif pass != passCmd2: setCommandEarly(config, p.key)
else:
if pass == passCmd1: config.commandArgs.add p.key
if argsCount == 1:
@@ -1326,6 +1142,4 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
config.projectName = unixToNativePath(p.key)
config.arguments = cmdLineRest(p)
result = true
else:
result = false
inc argsCount

View File

@@ -1,28 +0,0 @@
include "../lib/system/compilation.nim"
version = $NimMajor & "." & $NimMinor & "." & $NimPatch
author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library."
license = "MIT"
skipDirs = @["."]
installDirs = @["compiler"]
import os
var compilerDir = ""
before install:
rmDir("compiler")
let
files = listFiles(".")
dirs = listDirs(".")
mkDir("compiler")
for f in files:
cpFile(f, "compiler" / f)
for d in dirs:
cpDir(d, "compiler" / d)
requires "nim"

View File

@@ -11,15 +11,15 @@
## for details. Note this is a first implementation and only the "Concept matching"
## section has been implemented.
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets
import std/sets
from magicsys import addSonSkipIntLit
when defined(nimPreviewSlimSystem):
import std/assertions
const
logBindings = when defined(debugConcepts): true else: false
logBindings = false
## Code dealing with Concept declarations
## --------------------------------------
@@ -27,12 +27,24 @@ const
proc declareSelf(c: PContext; info: TLineInfo) =
## Adds the magical 'Self' symbols to the current scope.
let ow = getCurrOwner(c)
let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
s.typ = newType(tyTypeDesc, c.idgen, ow)
s.typ.incl {tfUnresolved, tfPacked}
s.typ.add newType(tyEmpty, c.idgen, ow)
let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info)
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
s.typ.flags.incl {tfUnresolved, tfPacked}
s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow)
addDecl(c, s, info)
proc isSelf*(t: PType): bool {.inline.} =
## Is this the magical 'Self' type?
t.kind == tyTypeDesc and tfPacked in t.flags
proc makeTypeDesc*(c: PContext, typ: PType): PType =
if typ.kind == tyTypeDesc and not isSelf(typ):
result = typ
else:
result = newTypeS(tyTypeDesc, c)
incl result.flags, tfCheckedForDestructor
result.addSonSkipIntLit(typ, c.idgen)
proc semConceptDecl(c: PContext; n: PNode): PNode =
## Recursive helper for semantic checking for the concept declaration.
## Currently we only support (possibly empty) lists of statements
@@ -50,7 +62,7 @@ proc semConceptDecl(c: PContext; n: PNode): PNode =
result[i] = n[i]
result[^1] = semConceptDecl(c, n[^1])
of nkCommentStmt:
result = n
discard
else:
localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
result = n
@@ -70,418 +82,150 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
## ----------------
type
MatchFlags* = enum
mfDontBind # Do not bind generic parameters
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
ConceptTypePair = tuple[conceptId, typeId: ItemId]
## Pair of (concept type id, implementation type id) used for cycle detection
MatchCon = object ## Context we pass around during concept matching.
bindings: LayeredIdTable
marker: HashSet[ConceptTypePair] ## Tracks (concept, type) pairs being checked to detect cycles.
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
## that turned out to be wrong.
marker: IntSet ## Some protection against wild runaway recursions.
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
## cannot be fixed that easily.
## Thus we special case it here.
concpt: PType ## current concept being evaluated
flags: set[MatchFlags]
MatchKind = enum
mkNoMatch, mkSubset, mkSame
const
asymmetricConceptParamMods = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred} # param modifiers that to not have to match implementation -> concept
bindableTypes = {tyGenericParam, tyOr, tyTypeDesc}
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool
proc existingBinding(m: MatchCon; key: PType): PType =
## checks if we bound the type variable 'key' already to some
## concrete type.
result = m.bindings.lookup(key)
if result == nil:
result = key
for i in 0..<m.inferred.len:
if m.inferred[i][0] == key: return m.inferred[i][1]
return nil
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
proc unrollGenericParam(param: PType): PType =
result = param.skipTypes(ignorableForArgType)
while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
result = result.elementType
proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
if v.kind == tyTypeDesc:
return false
var value = unrollGenericParam(v)
if value.kind == tyGenericParam:
value = existingBinding(m, value)
if value.kind == tyGenericParam:
if value.hasElementType:
value = value.elementType
else:
return true
if value.kind == tyStatic:
return false
if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
value = value.last
let old = existingBinding(m, key)
if old != key:
# check previously bound value
if not matchType(c, old, value, m):
return false
elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone:
# check constaint
if matchType(c, unrollGenericParam(key), value, m) == false:
return false
when logBindings: echo "bind table adding '", key, "', ", value
assert value != nil
assert value.kind != tyVoid
m.bindings.put(key, value)
return true
proc defSignatureType(n: PNode): PType = n[0].sym.typ
proc conceptBody*(n: PType): PNode = n.n.lastSon
proc acceptsAllTypes(t: PType): bool=
result = false
if t.kind == tyAnything:
result = true
elif t.kind == tyGenericParam:
if tfImplicitTypeParam in t.flags:
result = true
if not t.hasElementType or t.elementType.kind == tyNone:
result = true
proc procDefSignature(s: PSym): PNode {. deprecated .} =
var nc = s.ast.copyNode()
for i in 0 .. 5:
nc.add s.ast[i]
nc
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
result = true
for i in start ..< f.kidsLen - ord(f.kind in {tyGenericInst, tyGenericInvocation}):
if not matchType(c, f[i], a[i], m): return false
iterator traverseTyOr(t: PType): PType {. closure .}=
for i in t.kids:
case i.kind:
of tyGenericParam:
if i.hasElementType:
for s in traverseTyOr(i.elementType):
yield s
else:
yield i
else:
yield i
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
assert not(potentialImpl.reduceToBase.kind == tyConcept)
let concpt = f.reduceToBase
# Handle self-referential concepts: when a concept references itself in its body
# (e.g., `A = concept; proc test(x: Self, y: A)`), the inner type A has n=nil.
# We detect this by checking if the concept has the same symbol name as the
# one we're currently matching and has no body (n=nil).
if concpt.n.isNil:
if concpt.sym != nil and m.concpt.sym != nil and
concpt.sym == m.concpt.sym:
# Self-reference: check if potentialImpl matches what we're already checking
return potentialImpl.id == m.potentialImplementation.id
# Concept without body that's not a self-reference - cannot match
return false
# Cycle detection: track (concept, type) pairs to prevent infinite recursion.
# Returns true on cycle (coinductive semantics) to support co-dependent concepts.
let pair: ConceptTypePair = (concpt.itemId, potentialImpl.itemId)
if pair in m.marker:
return true
m.marker.incl pair
var efPot = potentialImpl
if potentialImpl.isSelf:
if m.concpt.n == concpt.n:
m.marker.excl pair
return true
efPot = m.potentialImplementation
var oldBindings = m.bindings
m.bindings = newTypeMapLayer(m.bindings)
let oldPotentialImplementation = m.potentialImplementation
m.potentialImplementation = efPot
let oldConcept = m.concpt
m.concpt = concpt
var invocation: PType = nil
if f.kind in {tyGenericInvocation, tyGenericInst}:
invocation = f
result = processConcept(c, concpt, invocation, oldBindings, m)
m.potentialImplementation = oldPotentialImplementation
m.concpt = oldConcept
m.bindings = oldBindings
m.marker.excl pair
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
if fn.kind != an.kind:
return false
if fn[namePos].sym.name != an[namePos].sym.name:
return false
let
ft = fn.defSignatureType
at = an.defSignatureType
if ft.len != at.len:
return false
for i in 1 ..< ft.n.len:
m.bindings = m.bindings.newTypeMapLayer()
let aType = at.n[i].typ
let fType = ft.n[i].typ
if aType.isSelf and fType.isSelf:
continue
if not matchType(c, fType, aType, m):
m.bindings.setToPreviousLayer()
return false
result = true
if not matchReturnType(c, ft.returnType, at.returnType, m):
m.bindings.setToPreviousLayer()
result = false
proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
# XXX: In the future this may need extra parameters to carry info for container types
if fc.n == ac.n:
# This will have to take generic parameters into account at some point
return mkSame
let
fn = fc.conceptBody
an = ac.conceptBody
sameLen = fc.len == ac.len
var match = false
for fdef in fn:
var cmpResult = false
for ia, ndef in an:
match = cmpConceptDefs(c, fdef, ndef, m)
if match:
break
if not match:
return mkNoMatch
return mkSubset
proc isObjectSubtype(f, a: PType): bool =
var t = a
result = false
while t != nil:
t = t.baseClass
if t == nil:
break
t = t.skipTypes({tyPtr,tyRef})
if t == nil:
break
if t.kind != tyObject:
break
if sameObjectTypes(f, t):
result = true
break
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
## The heart of the concept matching process. 'f' is the formal parameter of some
## routine inside the concept that we're looking for. 'a' is the formal parameter
## of a routine that might match.
var
a = ao
f = fo
if a.isSelf:
if m.magic in {mArrPut, mArrGet}:
return false
a = m.potentialImplementation
if a.kind in bindableTypes:
a = existingBinding(m, ao)
if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
a = a.elementType
if f.isConcept:
if a.acceptsAllTypes:
return false
if a.skipTypes(ignorableForArgType).isConcept:
# if f is a subset of a then any match to a will also match f. Not the other way around
return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
else:
return matchConceptToImpl(c, f, a, m)
result = false
const
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
case f.kind
of tyAlias:
result = matchType(c, f.skipModifier, a, m)
result = matchType(c, f.lastSon, a, m)
of tyTypeDesc:
if isSelf(f):
let ua = a.skipTypes(asymmetricConceptParamMods)
if m.magic in {mArrPut, mArrGet}:
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
bindParam(c, m, a, last m.potentialImplementation)
result = true
#elif ua.isConcept:
# result = matchType(c, m.concpt, ua, m)
else:
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
#let oldLen = m.inferred.len
result = matchType(c, a, m.potentialImplementation, m)
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
#m.inferred.setLen oldLen
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
else:
if a.kind == tyTypeDesc:
if not(a.hasElementType) or a.elementType.kind == tyNone:
if a.kind == tyTypeDesc and f.len == a.len:
for i in 0..<a.len:
if not matchType(c, f[i], a[i], m): return false
return true
of tyGenericInvocation:
if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
if sameType(f[0], a[0]) and f.len == a.len-1:
for i in 1 ..< f.len:
if not matchType(c, f[i], a[i], m): return false
return true
of tyGenericParam:
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
result = false
else:
let old = existingBinding(m, f)
if old == nil:
if f.len > 0 and f[0].kind != tyNone:
# also check the generic's constraints:
let oldLen = m.inferred.len
result = matchType(c, f[0], a, m)
m.inferred.setLen oldLen
if result:
when logBindings: echo "A adding ", f, " ", ak
m.inferred.add((f, ak))
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
when logBindings: echo "B adding ", f, " ", lastSon ak
m.inferred.add((f, lastSon ak))
result = true
elif f.hasElementType:
result = matchType(c, f.elementType, a.elementType, m)
else:
when logBindings: echo "C adding ", f, " ", ak
m.inferred.add((f, ak))
#echo "binding ", typeToString(ak), " to ", typeToString(f)
result = true
elif not m.marker.containsOrIncl(old.id):
result = matchType(c, old, ak, m)
if m.magic == mArrPut and ak.kind == tyGenericParam:
result = true
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
of tyVar, tySink, tyLent, tyOwned:
# modifiers in the concept must be there in the actual implementation
# too but not vice versa.
if a.kind == f.kind:
result = matchType(c, f.elementType, a.elementType, m)
result = matchType(c, f.sons[0], a.sons[0], m)
elif m.magic == mArrPut:
result = matchType(c, f.elementType, a, m)
of tyEnum, tyObject, tyDistinct:
if a.kind in ignorableForArgType:
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
result = matchType(c, f.sons[0], a, m)
else:
if a.kind == tyGenericInst:
# tyOr does this to generic typeclasses
result = a.base.sym == f.sym
else:
result = sameType(f, a)
if not result and f.kind == tyObject and a.kind == tyObject:
result = isObjectSubtype(f, a)
result = false
of tyEnum, tyObject, tyDistinct:
result = sameType(f, a)
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
result = a.skipTypes(ignorableForArgType).kind == f.kind
of tyBool, tyChar, tyInt..tyUInt64:
let ak = a.skipTypes(ignorableForArgType)
result = ak.kind == f.kind or ak.kind == tyOrdinal or
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
# XXX: this is a work-around!
# system.nim creates these for the magic array typeclass
result = true
else:
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind:
if f.base.kind == tyNone:
result = true
elif f.kidsLen == ak.kidsLen:
result = matchKids(c, f, ak, m)
of tyGenericInvocation, tyGenericInst:
result = false
let ea = a.skipTypes(ignorableForArgType)
if ea.kind in {tyGenericInst, tyGenericInvocation}:
var
k1 = f.kidsLen - ord(f.kind == tyGenericInst)
k2 = ea.kidsLen - ord(ea.kind == tyGenericInst)
if sameType(f.genericHead, ea.genericHead) and k1 == k2:
result = true
for i in 1 ..< k2:
if not matchType(c, f[i], ea[i], m):
result = false
break
elif f.kind == tyGenericInvocation:
# bind potential generic constraints into body
let body = f.base
for i in 1 ..< len(f):
bindParam(c,m,body[i-1], f[i])
result = matchType(c, body, a, m)
else: # tyGenericInst
result = matchType(c, f.last, a, m)
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
of tyStatic:
var scomp = f.base
if scomp.kind == tyGenericParam:
if f.base.kidsLen > 0:
scomp = scomp.base
if a.kind == tyStatic:
result = matchType(c, scomp, a.base, m)
else:
result = matchType(c, scomp, a, m)
of tyGenericParam:
if a.acceptsAllTypes:
discard bindParam(c, m, f, a)
result = f.acceptsAllTypes
else:
result = bindParam(c, m, f, a)
of tyAnything:
result = true
of tyNot:
if a.kind == tyNot:
result = matchType(c, f.elementType, a.elementType, m)
else:
m.bindings = m.bindings.newTypeMapLayer()
result = not matchType(c, f.elementType, a, m)
m.bindings.setToPreviousLayer()
of tyAnd:
m.bindings = m.bindings.newTypeMapLayer()
result = true
for ff in traverseTyOr(f):
let r = matchType(c, ff, a, m)
if not r:
m.bindings.setToPreviousLayer()
result = false
break
of tyGenericBody:
var ak = a
if a.kind == tyGenericBody:
ak = last(a)
result = matchType(c, last(f), ak, m)
of tyCompositeTypeClass:
if a.kind == tyCompositeTypeClass:
result = matchKids(c, f, a, m)
else:
result = matchType(c, last(f), a, m)
of tyBuiltInTypeClass:
let target = f.genericHead.kind
result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
(ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal)
of tyConcept:
let oldLen = m.inferred.len
let oldPotentialImplementation = m.potentialImplementation
m.potentialImplementation = a
result = conceptMatchNode(c, f.n.lastSon, m)
m.potentialImplementation = oldPotentialImplementation
if not result:
m.inferred.setLen oldLen
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
tyGenericInst:
let ak = a.skipTypes(ignorableForArgType - {f.kind})
if ak.kind == f.kind and f.len == ak.len:
for i in 0..<ak.len:
if not matchType(c, f[i], ak[i], m): return false
return true
of tyOr:
let oldLen = m.inferred.len
if a.kind == tyOr:
# say the concept requires 'int|float|string' if the potentialImplementation
# says 'int|string' that is good enough.
var covered = 0
for ff in traverseTyOr(f):
for aa in traverseTyOr(a):
m.bindings = m.bindings.newTypeMapLayer()
let r = matchType(c, ff, aa, m)
for i in 0..<f.len:
for j in 0..<a.len:
let oldLenB = m.inferred.len
let r = matchType(c, f[i], a[j], m)
if r:
inc covered
break
m.bindings.setToPreviousLayer()
m.inferred.setLen oldLenB
result = covered >= a.kidsLen
result = covered >= a.len
if not result:
m.inferred.setLen oldLen
else:
for ff in f.kids:
m.bindings = m.bindings.newTypeMapLayer()
result = matchType(c, ff, a, m)
for i in 0..<f.len:
result = matchType(c, f[i], a, m)
if result: break # and remember the binding!
m.bindings.setToPreviousLayer()
of tySet:
result = false
if a.kind == tySet:
result = matchType(c, f.elementType, a.elementType, m)
m.inferred.setLen oldLen
of tyNot:
if a.kind == tyNot:
result = matchType(c, f[0], a[0], m)
else:
let oldLen = m.inferred.len
result = not matchType(c, f[0], a, m)
m.inferred.setLen oldLen
of tyAnything:
result = true
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
else:
result = false
if result and ao.kind == tyGenericParam:
let bf = if f.isSelf: m.potentialImplementation else: f
if bindParam(c, m, ao, bf):
when logBindings: echo " ^ reverse binding"
proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
result = matchType(c, f, a, m) or matchType(c, a, f, m)
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
## Like 'matchType' but with extra logic dealing with proc return types
@@ -491,38 +235,30 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
elif a == nil:
result = false
else:
result = checkConstraint(c, f, a, m)
result = matchType(c, f, a, m)
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
## or similar.
# watch out: only add bindings after a completely successful match.
m.bindings = m.bindings.newTypeMapLayer()
let oldLen = m.inferred.len
let can = candidate.typ.n
let con = defSignatureType(n).n
let con = n[0].sym.typ.n
if can.len < con.len:
# too few arguments, cannot be a match:
return false
if can.len > con.len:
# too many arguments (not optional)
for i in con.len ..< can.len:
if can[i].sym.ast == nil:
return false
when defined(debugConcepts):
echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
let common = min(can.len, con.len)
for i in 1 ..< common:
if not checkConstraint(c, con[i].typ, can[i].typ, m):
m.bindings.setToPreviousLayer()
if not matchType(c, con[i].typ, can[i].typ, m):
m.inferred.setLen oldLen
return false
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
m.bindings.setToPreviousLayer()
if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m):
m.inferred.setLen oldLen
return false
# all other parameters have to be optional parameters:
@@ -530,7 +266,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
assert can[i].kind == nkSym
if can[i].sym.ast == nil:
# has too many arguments one of which is not optional:
m.bindings.setToPreviousLayer()
m.inferred.setLen oldLen
return false
return true
@@ -538,14 +274,12 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
## 'n' is the nkProcDef or similar from the concept that we try to match.
result = false
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
let candidates = searchInScopesFilterBy(c, n[namePos].sym.name, kinds)
for candidate in candidates:
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
m.magic = candidate.magic
if matchSym(c, candidate, n, m):
result = true
break
if matchSym(c, candidate, n, m): return true
result = false
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
@@ -572,72 +306,39 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
result = matchSyms(c, n, {skMethod}, m)
of nkIteratorDef:
result = matchSyms(c, n, {skIterator}, m)
of nkCommentStmt:
result = true
else:
# error was reported earlier.
result = false
proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; m: var MatchCon) =
# invocation != nil means we have a non-atomic concept:
if invocation != nil and invocation.kind == tyGenericInvocation:
assert concpt.sym.typ.kind == tyGenericBody
for i in 0 .. concpt.sym.typ.len - 1:
let thisSym = concpt.sym.typ[i]
if lookup(bindings, thisSym) != nil:
# dont trust the bindings over existing ones
continue
let found = m.bindings.lookup(thisSym)
if found != nil:
when logBindings: echo "Invocation bind: ", thisSym, " ", found
bindings.put(thisSym, found)
# bind even more generic parameters
let genBody = invocation.base
assert genBody.kind == tyGenericBody
for i in FirstGenericParamAt ..< invocation.kidsLen:
let bpram = genBody[i - 1]
if lookup(bindings, invocation[i]) != nil:
# dont trust the bindings over existing ones
continue
let boundV = lookup(bindings, bpram)
when logBindings: echo "generic body bind: '", invocation[i], "' '", boundV, "'"
if boundV != nil:
bindings.put(invocation[i], boundV)
bindings.put(concpt, m.potentialImplementation)
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool =
m.bindings = m.bindings.newTypeMapLayer()
if invocation != nil and invocation.kind == tyGenericInst:
let genericBody = invocation.base
for i in 1..<invocation.kidsLen-1:
# instGenericContainer can bind `tyVoid`
if invocation[i].kind != tyVoid:
bindParam(c, m, genericBody[i-1], invocation[i])
result = conceptMatchNode(c, concpt.conceptBody, m)
if result and mfDontBind notin m.flags:
fixBindings(bindings, concpt, invocation, m)
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool =
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool =
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
## we do not add any bindings at all on an unsuccessful match!
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags, marker: initHashSet[ConceptTypePair]())
if arg.isConcept:
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
elif arg.acceptsAllTypes:
# XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
result = false
elif mfCheckGeneric in m.flags:
# prioritize concepts the least. Specifically if the arg is not a catch all as per above
result = true
else:
result = processConcept(c, concpt, invocation, bindings, m)
var m = MatchCon(inferred: @[], potentialImplementation: arg)
result = conceptMatchNode(c, concpt.n.lastSon, m)
if result:
for (a, b) in m.inferred:
if b.kind == tyGenericParam:
var dest = b
while true:
dest = existingBinding(m, dest)
if dest == nil or dest.kind != tyGenericParam: break
if dest != nil:
bindings.idTablePut(a, dest)
when logBindings: echo "A bind ", a, " ", dest
else:
bindings.idTablePut(a, b)
when logBindings: echo "B bind ", a, " ", b
# we have a match, so bind 'arg' itself to 'concpt':
bindings.idTablePut(concpt, arg)
# invocation != nil means we have a non-atomic concept:
if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1:
# bind even more generic parameters
assert invocation.kind == tyGenericInvocation
for i in 1 ..< invocation.len:
bindings.idTablePut(invocation[i], arg[i])

View File

@@ -10,7 +10,7 @@
# This module handles the conditional symbols.
import
std/strtabs
strtabs
from options import Feature
from lineinfos import hintMin, hintMax, warnMin, warnMax
@@ -25,7 +25,7 @@ proc undefSymbol*(symbols: StringTableRef; symbol: string) =
# result = if isDefined(symbol): gSymbols[symbol] else: nil
iterator definedSymbolNames*(symbols: StringTableRef): string =
for key in keys(symbols):
for key, val in pairs(symbols):
yield key
proc countDefinedSymbols*(symbols: StringTableRef): int =
@@ -45,8 +45,10 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimNewTypedesc") # deadcode
defineSymbol("nimrequiresnimframe") # deadcode
defineSymbol("nimparsebiggestfloatmagic") # deadcode
defineSymbol("nimalias") # deadcode
defineSymbol("nimlocks") # deadcode
defineSymbol("nimnode") # deadcode
defineSymbol("nimnode") # deadcode pending `nimnode` reference in opengl package
# refs https://github.com/nim-lang/opengl/pull/79
defineSymbol("nimvarargstyped") # deadcode
defineSymbol("nimtypedescfixed") # deadcode
defineSymbol("nimKnowsNimvm") # deadcode
@@ -82,24 +84,10 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasSignatureHashInMacro") # deadcode
defineSymbol("nimHasDefault") # deadcode
defineSymbol("nimMacrosSizealignof") # deadcode
defineSymbol("nimNoZeroExtendMagic") # deadcode
defineSymbol("nimMacrosGetNodeId") # deadcode
defineSymbol("nimFixedForwardGeneric") # deadcode
defineSymbol("nimToOpenArrayCString") # deadcode
defineSymbol("nimHasUsed") # deadcode
defineSymbol("nimnomagic64") # deadcode
defineSymbol("nimNewShiftOps") # deadcode
defineSymbol("nimHasCursor") # deadcode
defineSymbol("nimAlignPragma") # deadcode
defineSymbol("nimHasExceptionsQuery") # deadcode
defineSymbol("nimHasIsNamedTuple") # deadcode
defineSymbol("nimHashOrdinalFixed") # deadcode
defineSymbol("nimHasSinkInference") # deadcode
defineSymbol("nimNewIntegerOps") # deadcode
defineSymbol("nimHasInvariant") # deadcode
# > 0.20.0
defineSymbol("nimNoZeroExtendMagic")
defineSymbol("nimMacrosGetNodeId")
for f in Feature:
defineSymbol("nimHas" & $f)
@@ -110,18 +98,31 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimFixedOwned")
defineSymbol("nimHasStyleChecks")
defineSymbol("nimToOpenArrayCString")
defineSymbol("nimHasUsed")
defineSymbol("nimFixedForwardGeneric")
defineSymbol("nimnomagic64")
defineSymbol("nimNewShiftOps")
defineSymbol("nimHasCursor")
defineSymbol("nimAlignPragma")
defineSymbol("nimHasExceptionsQuery")
defineSymbol("nimHasIsNamedTuple")
defineSymbol("nimHashOrdinalFixed")
when defined(nimHasLibFFI):
# Renaming as we can't conflate input vs output define flags; e.g. this
# will report the right thing regardless of whether user adds
# `-d:nimHasLibFFI` in his user config.
defineSymbol("nimHasLibFFIEnabled") # deadcode
defineSymbol("nimHasLibFFIEnabled")
defineSymbol("nimHasStacktraceMsgs") # deadcode
defineSymbol("nimHasSinkInference")
defineSymbol("nimNewIntegerOps")
defineSymbol("nimHasInvariant")
defineSymbol("nimHasStacktraceMsgs")
defineSymbol("nimDoesntTrackDefects")
defineSymbol("nimHasLentIterators") # deadcode
defineSymbol("nimHasDeclaredMagic") # deadcode
defineSymbol("nimHasStacktracesModule") # deadcode
defineSymbol("nimHasLentIterators")
defineSymbol("nimHasDeclaredMagic")
defineSymbol("nimHasStacktracesModule")
defineSymbol("nimHasEffectTraitsModule")
defineSymbol("nimHasCastPragmaBlocks")
defineSymbol("nimHasDeclaredLocs")
@@ -132,8 +133,8 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasCustomLiterals")
defineSymbol("nimHasUnifiedTuple")
defineSymbol("nimHasIterable")
defineSymbol("nimHasTypeofVoid") # deadcode
defineSymbol("nimHasDragonBox") # deadcode
defineSymbol("nimHasTypeofVoid")
defineSymbol("nimHasDragonBox")
defineSymbol("nimHasHintAll")
defineSymbol("nimHasTrace")
defineSymbol("nimHasEffectsOf")
@@ -143,37 +144,8 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasTemplateRedefinitionPragma")
defineSymbol("nimHasCstringCase")
defineSymbol("nimHasCallsitePragma")
defineSymbol("nimHasAmbiguousEnumHint")
defineSymbol("nimHasWarnCastSizes") # deadcode
defineSymbol("nimHasOutParams")
defineSymbol("nimHasSystemRaisesDefect")
defineSymbol("nimHasWarnUnnamedBreak")
defineSymbol("nimHasGenericDefine")
defineSymbol("nimHasDefineAliases")
defineSymbol("nimHasWarnBareExcept")
defineSymbol("nimHasDup")
defineSymbol("nimHasChecksums")
defineSymbol("nimHasSendable")
defineSymbol("nimAllowNonVarDestructor")
defineSymbol("nimHasQuirky")
defineSymbol("nimHasEnsureMove")
defineSymbol("nimHasNoReturnError")
defineSymbol("nimUseStrictDefs") # deadcode
defineSymbol("nimHasNolineTooLong")
defineSymbol("nimHasCastExtendedVm")
defineSymbol("nimHasWarnStdPrefix")
defineSymbol("nimHasVtables")
defineSymbol("nimHasGenericsOpenSym2")
defineSymbol("nimHasGenericsOpenSym3")
defineSymbol("nimHasJsNoLambdaLifting")
defineSymbol("nimHasDefaultFloatRoundtrip")
defineSymbol("nimHasXorSet")
defineSymbol("nimHasSetLengthSeqUninitMagic")
defineSymbol("nimHasPreviewDuplicateModuleError")
defineSymbol("nimHasImplicitRangeConversion")

View File

@@ -9,12 +9,12 @@
# This module implements a dependency file generator.
import options, ast, ropes, pathutils, msgs, lineinfos
import options, ast, ropes, passes, pathutils, msgs, lineinfos
import modulegraphs
import std/[os, parseutils]
import std/strutils except addf
import strutils except addf
import std/private/globs
when defined(nimPreviewSlimSystem):
@@ -42,7 +42,7 @@ proc toNimblePath(s: string, isStdlib: bool): string =
let sub = "lib/"
var start = s.find(sub)
if start < 0:
raiseAssert "unreachable"
doAssert false
else:
start += sub.len
let base = s[start..^1]
@@ -63,16 +63,12 @@ proc toNimblePath(s: string, isStdlib: bool): string =
sub.add "/pkgs/"
var start = s.find(sub)
if start < 0:
sub[^1] = '2'
sub.add '/'
start = s.find(sub) # /pkgs2
if start < 0:
return s
start += sub.len
start += skipUntil(s, '/', start)
start += 1
result = pkgPrefix & s[start..^1]
result = s
else:
start += sub.len
start += skipUntil(s, '/', start)
start += 1
result = pkgPrefix & s[start..^1]
proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
doAssert n.kind == nkSym, $n.kind
@@ -83,7 +79,7 @@ proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
addDependencyAux(b, parent, child)
proc addDotDependency*(c: PPassContext, n: PNode): PNode =
proc addDotDependency(c: PPassContext, n: PNode): PNode =
result = n
let g = PGen(c)
let b = Backend(g.graph.backend)
@@ -104,7 +100,18 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
rope(project.splitFile.name), b.dotGraph],
changeFileExt(project, "dot"))
proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
result = PGen(module: module, config: graph.config, graph: graph)
when not defined(nimHasSinkInference):
{.pragma: nosinks.}
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
var g: PGen
new(g)
g.module = module
g.config = graph.config
g.graph = graph
if graph.backend == nil:
graph.backend = Backend(dotGraph: "")
result = g
const gendependPass* = makePass(open = myOpen, process = addDotDependency)

File diff suppressed because it is too large Load Diff

View File

@@ -22,9 +22,8 @@
## "A GraphFree Approach to DataFlow Analysis" by Markus Mohnen.
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
import ast, lineinfos, renderer, aliasanalysis
import ast, intsets, lineinfos, renderer, aliasanalysis
import std/private/asciitables
import std/intsets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -46,10 +45,10 @@ type
case isTryBlock: bool
of false:
label: PSym
breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales
breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
of true:
finale: PNode
raiseFixups: seq[TPosition] # Contains the gotos for the raises
raiseFixups: seq[TPosition] #Contains the gotos for the raises
Con = object
code: ControlFlowGraph
@@ -61,7 +60,6 @@ type
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
# for debugging purposes
# first iteration: compute all necessary labels:
result = ""
var jumpTargets = initIntSet()
let last = if last < 0: c.len-1 else: min(last, c.len-1)
for i in start..last:
@@ -113,7 +111,7 @@ proc patch(c: var Con, p: TPosition) =
proc gen(c: var Con; n: PNode)
proc popBlock(c: var Con; oldLen: int) =
var exits: seq[TPosition] = @[]
var exits: seq[TPosition]
exits.add c.gotoI()
for f in c.blocks[oldLen].breakFixups:
c.patch(f[0])
@@ -130,6 +128,10 @@ template withBlock(labl: PSym; body: untyped) =
body
popBlock(c, oldLen)
proc isTrue(n: PNode): bool =
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
n.kind == nkIntLit and n.intVal != 0
template forkT(body) =
let lab1 = c.forkI()
body
@@ -181,6 +183,14 @@ proc genIf(c: var Con, n: PNode) =
goto Lend3
L3:
D
goto Lend3 # not eliminated to simplify the join generation
Lend3:
join F3
Lend2:
join F2
Lend:
join F1
]#
var endings: seq[TPosition] = @[]
let oldInteresting = c.interestingInstructions
@@ -205,6 +215,7 @@ proc genAndOr(c: var Con; n: PNode) =
# fork lab1
# asgn dest, b
# lab1:
# join F1
c.gen(n[1])
forkT:
c.gen(n[2])
@@ -252,9 +263,8 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
if c.blocks[i].isTryBlock:
c.blocks[i].raiseFixups.add lab1
else:
var trailingFinales: seq[PNode] = @[]
if c.inTryStmt > 0:
# Ok, we are in a try, lets see which (if any) try's we break out from:
var trailingFinales: seq[PNode]
if c.inTryStmt > 0: #Ok, we are in a try, lets see which (if any) try's we break out from:
for b in countdown(c.blocks.high, i):
if c.blocks[b].isTryBlock:
trailingFinales.add c.blocks[b].finale
@@ -315,7 +325,7 @@ proc genRaise(c: var Con; n: PNode) =
if c.blocks[i].isTryBlock:
genBreakOrRaiseAux(c, i, n)
return
assert false # Unreachable
assert false #Unreachable
else:
genNoReturn(c)
@@ -371,16 +381,16 @@ proc genCall(c: var Con; n: PNode) =
if t != nil: t = t.skipTypes(abstractInst)
for i in 1..<n.len:
gen(c, n[i])
if t != nil and i < t.signatureLen and isOutParam(t[i]):
if t != nil and i < t.len and isOutParam(t[i]):
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
if c.inTryStmt > 0 and canRaiseConservative(n[0]):
inc c.interestingInstructions
if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]):
# we generate the instruction sequence:
# fork lab1
# goto exceptionHandler (except or finally)
# lab1:
# join F1
forkT:
for i in countdown(c.blocks.high, 0):
if c.blocks[i].isTryBlock:
@@ -439,8 +449,8 @@ proc gen(c: var Con; n: PNode) =
genUse(c, n)
of nkIfStmt, nkIfExpr: genIf(c, n)
of nkWhenStmt:
# This is "when nimvm" node. Chose the second branch.
gen(c, n[1][0])
# This is "when nimvm" node. Chose the first branch.
gen(c, n[0][1])
of nkCaseStmt: genCase(c, n)
of nkWhileStmt: genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
@@ -457,7 +467,7 @@ proc gen(c: var Con; n: PNode) =
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
gen(c, n[1])
of nkVarSection, nkLetSection: genVarSection(c, n)
of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'"
of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
else: discard
when false:
@@ -483,7 +493,7 @@ proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
gen(c, body)
if root.kind == skResult:
genImplicitReturn(c)
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc):
result = c.code # will move
else:
shallowCopy(result, c.code)

View File

@@ -7,24 +7,19 @@
# distribution, for details about the copyright.
#
## This is the Nim documentation generator. Cross-references are generated
## by knowing how the anchors are going to be named.
##
## .. importdoc:: ../docgen.md
##
## For corresponding users' documentation see [Nim DocGen Tools Guide].
# This is the documentation generator. Cross-references are generated
# by knowing how the anchors are going to be named.
import
ast, options, msgs, idents,
ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents,
wordrecg, syntaxes, renderer, lexer,
packages/docutils/[rst, rstidx, rstgen, dochelpers],
trees, types,
typesrenderer, lineinfos,
pathutils, nimpaths, renderverbatim, packages
packages/docutils/[rst, rstgen, dochelpers],
json, xmltree, trees, types,
typesrenderer, astalgo, lineinfos, intsets,
pathutils, tables, nimpaths, renderverbatim, osproc, packages
import packages/docutils/rstast except FileIndex, TLineInfo
import std/[os, strutils, strtabs, algorithm, json, osproc, tables, intsets, xmltree, sequtils]
from std/uri import encodeUrl
from uri import encodeUrl
from nodejs import findNodeJs
when defined(nimPreviewSlimSystem):
@@ -96,7 +91,7 @@ type
jEntriesFinal: JsonNode # final JSON after RST pass 2 and rendering
types: TStrTable
sharedState: PRstSharedState
standaloneDoc: bool # is markup (.rst/.md) document?
standaloneDoc: bool
conf*: ConfigRef
cache*: IdentCache
exampleCounter: int
@@ -113,16 +108,13 @@ type
proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst)
proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str)
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
let invalid = rstast.FileIndex(-1)
result = d.nimToRstFid.getOrDefault(fileIndex, invalid)
if result == invalid:
let fname = toFullPath(d.conf, fileIndex)
result = addFilename(d.sharedState, fname)
d.nimToRstFid[fileIndex] = result
proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex =
addRstFileIndex(d, info.fileIndex)
let invalid = rstast.FileIndex(-1)
result = d.nimToRstFid.getOrDefault(info.fileIndex, default = invalid)
if result == invalid:
let fname = toFullPath(d.conf, info)
result = addFilename(d.sharedState, fname)
d.nimToRstFid[info.fileIndex] = result
proc cmpDecimalsIgnoreCase(a, b: string): int =
## For sorting with correct handling of cases like 'uint8' and 'uint16'.
@@ -148,7 +140,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
limitB = iB
while limitA < aLen and isDigit(a[limitA]): inc limitA
while limitB < bLen and isDigit(b[limitB]): inc limitB
var pos = max(limitA-iA, limitB-iB)
var pos = max(limitA-iA, limitB-iA)
while pos > 0:
if limitA-pos < iA: # digit in `a` is 0 effectively
result = ord('0') - ord(b[limitB-pos])
@@ -171,7 +163,6 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
proc prettyString(a: object): string =
# xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906
result = ""
for k, v in fieldPairs(a):
result.add k & ": " & $v & "\n"
@@ -217,16 +208,12 @@ proc whichType(d: PDoc; n: PNode): PSym =
if n.kind == nkSym:
if d.types.strTableContains(n.sym):
result = n.sym
else:
result = nil
else:
result = nil
for i in 0..<n.safeLen:
let x = whichType(d, n[i])
if x != nil: return x
proc attachToType(d: PDoc; p: PSym): PSym =
result = nil
let params = p.ast[paramsPos]
template check(i) =
result = whichType(d, params[i])
@@ -238,7 +225,7 @@ proc attachToType(d: PDoc; p: PSym): PSym =
if params.len > 0: check(0)
for i in 2..<params.len: check(i)
template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
template declareClosures =
proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.MsgKind, arg: string) {.gcsafe.} =
# translate msg kind:
@@ -262,19 +249,9 @@ template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
of mwBrokenLink: k = warnRstBrokenLink
of mwUnsupportedLanguage: k = warnRstLanguageXNotSupported
of mwUnsupportedField: k = warnRstFieldXNotSupported
of mwUnusedImportdoc: k = warnRstUnusedImportdoc
of mwRstStyle: k = warnRstStyle
{.gcsafe.}:
let errorsAsWarnings = (roPreferMarkdown in d.sharedState.options) and
not d.standaloneDoc # not tolerate errors in .rst/.md files
if whichMsgClass(msgKind) == mcError and errorsAsWarnings:
liMessage(conf, newLineInfo(conf, AbsoluteFile filename, line, col),
k, arg, doNothing, instLoc(), ignoreError=true)
# when our Markdown parser fails, we currently can only terminate the
# parsing (and then we will return monospaced text instead of markup):
raiseRecoverableError("")
else:
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
proc docgenFindFile(s: string): string {.gcsafe.} =
result = options.findFile(conf, s).string
@@ -282,29 +259,10 @@ template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
result = getCurrentDir() / s
if not fileExists(result): result = ""
proc docgenFindRefFile(targetRelPath: string):
tuple[targetPath: string, linkRelPath: string] {.gcsafe.} =
let fromDir = splitFile(destFile).dir # dir where we reference from
let basedir = os.splitFile(currentFilename.string).dir
let outDirPath: RelativeFile =
presentationPath(conf, AbsoluteFile(basedir / targetRelPath))
# use presentationPath because `..` path can be be mangled to `_._`
result = (string(conf.outDir / outDirPath), "")
if not fileExists(result.targetPath):
# this can happen if targetRelPath goes to parent directory `OUTDIR/..`.
# Trying it, this may cause ambiguities, but allows us to insert
# "packages" into each other, which is actually used in Nim repo itself.
let destPath = fromDir / targetRelPath
if destPath != result.targetPath and fileExists(destPath):
result.targetPath = destPath
result.linkRelPath = relativePath(result.targetPath.splitFile.dir,
fromDir).replace('\\', '/')
proc parseRst(text: string,
line, column: int,
conf: ConfigRef, sharedState: PRstSharedState): PRstNode =
declareClosures()
result = rstParsePass1(text, line, column, sharedState)
proc getOutFile2(conf: ConfigRef; filename: RelativeFile,
@@ -325,10 +283,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
outExt: string = HtmlExt, module: PSym = nil,
standaloneDoc = false, preferMarkdown = true,
hasToc = true): PDoc =
let destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string
declareClosures()
new(result)
let d = result # pass `d` to `declareClosures`:
declareClosures(currentFilename = filename, destFile = destFile)
result.module = module
result.conf = conf
result.cache = cache
@@ -342,14 +298,14 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
result.hasToc = hasToc
result.sharedState = newRstSharedState(
options, filename.string,
docgenFindFile, docgenFindRefFile, compilerMsgHandler, hasToc)
docgenFindFile, compilerMsgHandler, hasToc)
initRstGenerator(result[], (if conf.isLatexCmd: outLatex else: outHtml),
conf.configVars, filename.string,
docgenFindFile, compilerMsgHandler)
if conf.configVars.hasKey("doc.googleAnalytics") and
conf.configVars.hasKey("doc.plausibleAnalytics"):
raiseAssert "Either use googleAnalytics or plausibleAnalytics"
doAssert false, "Either use googleAnalytics or plausibleAnalytics"
if conf.configVars.hasKey("doc.googleAnalytics"):
result.analytics = """
@@ -374,7 +330,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100
result.jEntriesFinal = newJArray()
result.types = initStrTable()
initStrTable result.types
result.onTestSnippet =
proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} =
if conf.docCmd == docCmdSkip: return
@@ -417,7 +373,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
if gotten != status:
rawMessage(conf, errGenerated, "snippet failed: cmd: '$1' status: $2 expected: $3 output: $4" % [cmd, $gotten, $status, output])
result.emitted = initIntSet()
result.destFile = destFile
result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string
result.thisDir = result.destFile.AbsoluteFile.splitFile.dir
template dispA(conf: ConfigRef; dest: var string, xml, tex: string,
@@ -433,25 +389,17 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
proc genComment(d: PDoc, n: PNode): PRstNode =
if n.comment.len > 0:
if optDocRaw in d.conf.globalOptions:
return newRstLeaf(n.comment)
d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
try:
result = parseRst(n.comment,
toLinenumber(n.info),
toColumn(n.info) + DocColOffset,
d.conf, d.sharedState)
except ERecoverableError:
result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)])
else:
result = nil
result = parseRst(n.comment,
toLinenumber(n.info),
toColumn(n.info) + DocColOffset,
d.conf, d.sharedState)
proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
if n == nil: return nil
result = genComment(d, n)
if result == nil:
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkTypeClassTy,
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef,
nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}:
# notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
for i in 0..<n.len:
@@ -480,7 +428,6 @@ proc getPlainDocstring(n: PNode): string =
elif startsWith(n.comment, "##"):
result = n.comment
else:
result = ""
for i in 0..<n.safeLen:
result = getPlainDocstring(n[i])
if result.len > 0: return
@@ -496,8 +443,9 @@ proc externalDep(d: PDoc; module: PSym): string =
proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
renderFlags: TRenderFlags = {};
procLink: string) =
var r: TSrcGen = initTokRender(n, renderFlags)
var r: TSrcGen
var literal = ""
initTokRender(r, n, renderFlags)
var kind = tkEof
var tokenPos = 0
var procTokenPos = 0
@@ -540,11 +488,10 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
elif s != nil and s.kind in {skType, skVar, skLet, skConst} and
sfExported in s.flags and s.owner != nil and
belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml:
let href = (if d.module == s.owner: ""
else: externalDep(d, s.owner).changeFileExt("html")
) & "#" & literal
result.addf "<a href=\"$1\"><span class=\"Identifier\">$2</span></a>",
[href, escLit]
let external = externalDep(d, s.owner)
result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
[changeFileExt(external, "html"), literal,
escLit]
else:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [escLit])
@@ -590,13 +537,10 @@ proc runAllExamples(d: PDoc) =
# most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
# via command line
# D20210224T221756:here
var pathArgs = "--path:$path" % [ "path", quoteShell(d.conf.projectPath) ]
for p in d.conf.searchPaths:
pathArgs = "$args --path:$path" % [ "args", pathArgs, "path", quoteShell(p) ]
let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off $pathArgs --nimcache:$nimcache $rdoccmd $docCmd $file" % [
let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [
"nim", quoteShell(os.getAppFilename()),
"backend", $d.conf.backend,
"pathArgs", pathArgs,
"path", quoteShell(d.conf.projectPath),
"libpath", quoteShell(d.conf.libpath),
"nimcache", quoteShell(outputDir),
"file", quoteShell(outp),
@@ -612,11 +556,9 @@ proc runAllExamples(d: PDoc) =
rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
# removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
proc quoted(a: string): string =
result = ""
result.addQuoted(a)
proc quoted(a: string): string = result.addQuoted(a)
proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) =
proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): auto =
# xxx expose in compiler/lineinfos.nim
(conf.toMsgFilename(info), info.line.int, info.col.int + ColOffset)
@@ -633,6 +575,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
let useRenderModule = false
let loc = d.conf.toFileLineCol(n.info)
let code = extractRunnableExamplesSource(d.conf, n)
let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
if d.conf.errorCounter > 0:
return (rdoccmd, code)
@@ -648,7 +591,6 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
docComment.comment = comment
var runnableExamples = newTree(nkStmtList,
docComment,
newTree(nkImportStmt, newStrNode(nkStrLit, "std/assertions")),
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
runnableExamples.info = n.info
for a in n.lastSon: runnableExamples.add a
@@ -662,7 +604,6 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
else:
var code2 = code
if code.len > 0 and "codeReordering" notin code:
let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
# hacky but simplest solution, until we devise a way to make `{.line.}`
# work without introducing a scope
code2 = """
@@ -673,7 +614,6 @@ $#
#[
$#
]#
import std/assertions
import $#
$#
""" % [comment, d.filename.quoted, code2]
@@ -740,7 +680,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre,
let (rdoccmd, code) = prepareExample(d, n, topLevel)
var msg = "Example:"
if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
var s: string = ""
var s: string
dispA(d.conf, s, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
"\n\n\\textbf{$1}\n", [msg])
dest.add s
@@ -825,24 +765,21 @@ proc isVisible(d: PDoc; n: PNode): bool =
elif n.kind == nkPragmaExpr:
result = isVisible(d, n[0])
proc getName(n: PNode): string =
proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
case n.kind
of nkPostfix: result = getName(n[1])
of nkPragmaExpr: result = getName(n[0])
of nkSym: result = n.sym.renderDefinitionName
of nkIdent: result = n.ident.s
of nkPostfix: result = getName(d, n[1], splitAfter)
of nkPragmaExpr: result = getName(d, n[0], splitAfter)
of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter)
of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
of nkAccQuoted:
result = "`"
for i in 0..<n.len: result.add(getName(n[i]))
result.add('`')
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
result = getName(n[0])
result = esc(d.target, "`")
for i in 0..<n.len: result.add(getName(d, n[i], splitAfter))
result.add esc(d.target, "`")
of nkOpenSymChoice, nkClosedSymChoice:
result = getName(d, n[0], splitAfter)
else:
result = ""
proc getNameEsc(d: PDoc, n: PNode): string =
esc(d.target, getName(n))
proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
case n.kind
of nkPostfix: result = getNameIdent(cache, n[1])
@@ -853,7 +790,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
var r = ""
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
result = getIdent(cache, r)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
result = getNameIdent(cache, n[0])
else:
result = nil
@@ -867,7 +804,7 @@ proc getRstName(n: PNode): PRstNode =
of nkAccQuoted:
result = getRstName(n[0])
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
of nkOpenSymChoice, nkClosedSymChoice:
result = getRstName(n[0])
else:
result = nil
@@ -956,17 +893,14 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string =
if n[1].kind in {nkStrLit..nkTripleStrLit}:
result = getConfigVar(d.conf, "doc.deprecationmsg") % [
"label", "Deprecated:", "message", xmltree.escape(n[1].strVal)]
else:
result = ""
else:
raiseAssert "unreachable"
doAssert false
type DocFlags = enum
kDefault
kForceExport
proc genSeeSrc(d: PDoc, path: string, line: int): string =
result = ""
let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
if docItemSeeSrc.len > 0:
let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/')
@@ -994,22 +928,14 @@ proc symbolPriority(k: TSymKind): int =
else: 0 # including skProc which have higher priority
# documentation itself has even higher priority 1
proc getTypeKind(n: PNode): string =
case n[2].kind
of nkEnumTy: "enum"
of nkObjectTy: "object"
of nkTupleTy: "tuple"
else: ""
proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
## Converts symbol info (names/types/parameters) in `n` into format
## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``.
result = LangSymbol(name: baseName.nimIdentNormalize,
symKind: k.toHumanStr
)
result.name = baseName.nimIdentNormalize
result.symKind = k.toHumanStr
if k in routineKinds:
var
paramTypes: seq[string] = @[]
paramTypes: seq[string]
renderParamTypes(paramTypes, n[paramsPos], toNormalize=true)
let paramNames = renderParamNames(n[paramsPos], toNormalize=true)
# In some rare cases (system.typeof) parameter type is not set for default:
@@ -1034,8 +960,9 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
genNode = n[miscPos][1] # FIXME: what is index 1?
if genNode != nil:
var literal = ""
var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
var r: TSrcGen
initTokRender(r, genNode, {renderNoBody, renderNoComments,
renderNoPragmas, renderNoProcDefs})
var kind = tkEof
while true:
getNextTok(r, kind, literal)
@@ -1044,26 +971,31 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
if kind != tkSpaces:
result.generics.add(literal.nimIdentNormalize)
if k == skType: result.symTypeKind = getTypeKind(n)
if k == skType:
case n[2].kind
of nkEnumTy: result.symTypeKind = "enum"
of nkObjectTy: result.symTypeKind = "object"
of nkTupleTy: result.symTypeKind = "tuple"
else: discard
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonExports: bool = false) =
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
if (docFlags != kForceExport) and not isVisible(d, nameNode): return
let
name = getName(nameNode)
nameEsc = esc(d.target, name)
name = getName(d, nameNode)
var plainDocstring = getPlainDocstring(n) # call here before genRecComment!
var result = ""
var literal, plainName = ""
var kind = tkEof
var comm: ItemPre = default(ItemPre)
var comm: ItemPre
if n.kind in routineDefs:
getAllRunnableExamples(d, n, comm)
else:
comm.add genRecComment(d, n)
var r: TSrcGen
# Obtain the plain rendered string for hyperlink titles.
var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderNoProcDefs})
while true:
getNextTok(r, kind, literal)
if kind == tkEof:
@@ -1076,12 +1008,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
inc(d.id)
let
plainNameEsc = esc(d.target, plainName.strip)
typeDescr =
if k == skType and getTypeKind(n) != "": getTypeKind(n)
else: k.toHumanStr
detailedName = typeDescr & " " & (
detailedName = k.toHumanStr & " " & (
if k in routineKinds: plainName else: name)
uniqueName = if k in routineKinds: plainNameEsc else: nameEsc
uniqueName = if k in routineKinds: plainNameEsc else: name
sortName = if k in routineKinds: plainName.strip else: name
cleanPlainSymbol = renderPlainSymbolName(nameNode)
complexSymbol = complexName(k, n, cleanPlainSymbol)
@@ -1090,24 +1019,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false)
deprecationMsg = genDeprecationMsg(d, pragmaNode)
rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol)
symNameNode =
if nameNode.kind == nkPostfix: nameNode[1]
else: nameNode
# we generate anchors automatically for subsequent use in doc comments
let lineinfo = rstast.TLineInfo(
line: nameNode.info.line, col: nameNode.info.col,
fileIndex: addRstFileIndex(d, nameNode.info))
addAnchorNim(d.sharedState, external = false, refn = symbolOrId,
tooltip = detailedName, langSym = rstLangSymbol,
priority = symbolPriority(k), info = lineinfo,
module = addRstFileIndex(d, FileIndex d.module.position))
addAnchorNim(d.sharedState, refn = symbolOrId, tooltip = detailedName,
rstLangSymbol, priority = symbolPriority(k), info = lineinfo)
var renderFlags = {renderNoBody, renderNoComments, renderDocComments,
renderSyms, renderExpandUsing, renderNoPostfix}
if nonExports:
renderFlags.incl renderNonExportedFields
nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc)
nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments,
renderDocComments, renderSyms}, symbolOrIdEnc)
let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int)
@@ -1129,21 +1050,18 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string
var attype = ""
if k in routineKinds and symNameNode.kind == nkSym:
if k in routineKinds and nameNode.kind == nkSym:
let att = attachToType(d, nameNode.sym)
if att != nil:
attype = esc(d.target, att.name.s)
elif k == skType and symNameNode.kind == nkSym and
symNameNode.sym.typ.kind in {tyEnum, tyBool}:
let etyp = symNameNode.sym.typ
elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}:
let etyp = nameNode.sym.typ
for e in etyp.n:
if e.sym.kind != skEnumField: continue
let plain = renderPlainSymbolName(e)
let symbolOrId = d.newUniquePlainSymbol(plain)
setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId,
term = plain, linkTitle = symNameNode.sym.name.s & '.' & plain,
linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary),
line = n.info.line.int)
setIndexTerm(d[], external, symbolOrId, plain, nameNode.sym.name.s & '.' & plain,
xmltree.escape(getPlainDocstring(e).docstringSummary))
d.tocSimple[k].add TocItem(
sortName: sortName,
@@ -1158,34 +1076,30 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
"itemSymOrID", symbolOrId.replace(",", ",<wbr>"),
"itemSymOrIDEnc", symbolOrIdEnc])
setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, term = name,
linkTitle = detailedName,
linkDesc = xmltree.escape(plainDocstring.docstringSummary),
line = n.info.line.int)
if k == skType and symNameNode.kind == nkSym:
d.types.strTableAdd symNameNode.sym
# Ironically for types the complexSymbol is *cleaner* than the plainName
# because it doesn't include object fields or documentation comments. So we
# use the plain one for callable elements, and the complex for the rest.
var linkTitle = changeFileExt(extractFilename(d.filename), "") & ": "
if n.kind in routineDefs: linkTitle.add(xmltree.escape(plainName.strip))
else: linkTitle.add(xmltree.escape(complexSymbol.strip))
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem =
setIndexTerm(d[], external, symbolOrId, name, linkTitle,
xmltree.escape(plainDocstring.docstringSummary))
if k == skType and nameNode.kind == nkSym:
d.types.strTableAdd nameNode.sym
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem =
if not isVisible(d, nameNode): return
var
name = getNameEsc(d, nameNode)
name = getName(d, nameNode)
comm = genRecComment(d, n)
r: TSrcGen
renderFlags = {renderNoBody, renderNoComments, renderDocComments,
renderExpandUsing, renderNoPostfix}
if nonExports:
renderFlags.incl renderNonExportedFields
r = initTokRender(n, renderFlags)
result = JsonItem(json: %{ "name": %name, "type": %($k), "line": %n.info.line.int,
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col}
)
if comm != nil:
if optDocRaw in d.conf.globalOptions:
result.json["description"] = %comm.text
else:
result.rst = comm
result.rstField = "description"
result.rst = comm
result.rstField = "description"
if r.buf.len > 0:
result.json["code"] = %r.buf
if k in routineKinds:
@@ -1212,9 +1126,10 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
result.json["signature"]["genericParams"] = newJArray()
for genericParam in n[genericParamsPos]:
var param = %{"name": %($genericParam)}
if genericParam.sym.typ.len > 0:
if genericParam.sym.typ.sons.len > 0:
param["types"] = newJArray()
param["types"] = %($genericParam.sym.typ.elementType)
for kind in genericParam.sym.typ.sons:
param["types"].add %($kind)
result.json["signature"]["genericParams"].add param
if optGenIndex in d.conf.globalOptions:
genItem(d, n, nameNode, k, kForceExport)
@@ -1308,8 +1223,6 @@ proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
let s = n[namePos].sym
if tfReturnsNew in s.typ.flags:
result = newIdentNode(getIdent(cache, "new"), n.info)
else:
result = nil
proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
let spec = effectSpec(x, effectType)
@@ -1332,8 +1245,6 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
result = newTreeI(nkExprColonExpr, n.info,
newIdentNode(getIdent(cache, $effectType), n.info), effects)
else:
result = nil
proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
let s = n[namePos].sym
@@ -1347,8 +1258,6 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName
if effects.len > 0:
result = newTreeI(nkExprColonExpr, n.info,
newIdentNode(getIdent(cache, pragmaName), n.info), effects)
else:
result = nil
proc documentRaises*(cache: IdentCache; n: PNode) =
if n[namePos].kind != nkSym: return
@@ -1370,13 +1279,12 @@ proc documentRaises*(cache: IdentCache; n: PNode) =
if p5 != nil: n[pragmasPos].add p5
if p6 != nil: n[pragmasPos].add p6
proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags = kDefault) =
proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) =
## Goes through nim nodes recursively and collects doc comments.
## Main function for `doc`:option: command,
## which is implemented in ``docgen2.nim``.
template genItemAux(skind) =
genItem(d, n, n[namePos], skind, docFlags)
let showNonExports = optShowNonExportedFields in config.globalOptions
case n.kind
of nkPragma:
let pragmaNode = findPragma(n, wDeprecated)
@@ -1403,61 +1311,35 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
if n[i].kind != nkCommentStmt:
# order is always 'type var let const':
genItem(d, n[i], n[i][0],
succ(skType, ord(n.kind)-ord(nkTypeSection)), docFlags, showNonExports)
succ(skType, ord(n.kind)-ord(nkTypeSection)), docFlags)
of nkStmtList:
for i in 0..<n.len: generateDoc(d, n[i], orig, config)
for i in 0..<n.len: generateDoc(d, n[i], orig)
of nkWhenStmt:
# generate documentation for the first branch only:
if not checkForFalse(n[0][0]):
generateDoc(d, lastSon(n[0]), orig, config)
generateDoc(d, lastSon(n[0]), orig)
of nkImportStmt:
for it in n: traceDeps(d, it)
of nkExportStmt:
for it in n:
if it.kind == nkSym:
if d.module != nil and d.module == it.sym.owner: # in current module
# bug #23051; don't generate documentation for exported symbols again
if sfExported notin it.sym.flags:
generateDoc(d, it.sym.ast, orig, config, kForceExport)
# else it's to be handled in `of XxxSection` branch
if d.module != nil and d.module == it.sym.owner:
generateDoc(d, it.sym.ast, orig, kForceExport)
elif it.sym.ast != nil:
# only export symbols in imported modules, not in current module
exportSym(d, it.sym)
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
of nkCallKinds:
var comm = default(ItemPre)
var comm: ItemPre
getAllRunnableExamples(d, n, comm)
if comm.len != 0: d.modDescPre.add(comm)
else: discard
proc overloadGroupName(s: string, k: TSymKind): string =
## Turns a name like `f` into anchor `f-procs-all`
#s & " " & k.toHumanStr & "s all"
s & "-" & k.toHumanStr & "s-all"
proc setIndexTitle(d: PDoc, useMetaTitle: bool) =
let titleKind = if d.standaloneDoc: ieMarkupTitle else: ieNimTitle
let external = AbsoluteFile(d.destFile)
.relativeTo(d.conf.outDir, '/')
.changeFileExt(HtmlExt)
.string
var term, linkTitle: string
if useMetaTitle and d.meta[metaTitle].len != 0:
term = d.meta[metaTitleRaw]
linkTitle = d.meta[metaTitleRaw]
else:
let filename = extractFilename(d.filename)
term =
if d.standaloneDoc: filename # keep .rst/.md extension
else: changeFileExt(filename, "") # rm .nim extension
linkTitle =
if d.standaloneDoc: term # keep .rst/.md extension
else: canonicalImport(d.conf, AbsoluteFile d.filename)
if not d.standaloneDoc:
linkTitle = "module " & linkTitle
setIndexTerm(d[], titleKind, htmlFile = external, id = "",
term = term, linkTitle = linkTitle)
proc finishGenerateDoc*(d: var PDoc) =
## Perform 2nd RST pass for resolution of links/footnotes/headings...
# copy file map `filenames` to ``rstgen.nim`` for its warnings
@@ -1470,24 +1352,7 @@ proc finishGenerateDoc*(d: var PDoc) =
firstRst = fragment.rst
break
d.hasToc = d.hasToc or d.sharedState.hasToc
# in --index:only mode we do NOT want to load other .idx, only write ours:
let importdoc = optGenIndexOnly notin d.conf.globalOptions and
optNoImportdoc notin d.conf.globalOptions
preparePass2(d.sharedState, firstRst, importdoc)
if optGenIndexOnly in d.conf.globalOptions:
# Top-level doc.comments may contain titles and :idx: statements:
for fragment in d.modDescPre:
if fragment.isRst:
traverseForIndex(d[], fragment.rst)
setIndexTitle(d, useMetaTitle = d.standaloneDoc)
# Symbol-associated doc.comments may contain :idx: statements:
for k in TSymKind:
for _, overloadChoices in d.section[k].secItems:
for item in overloadChoices:
for fragment in item.descRst:
if fragment.isRst:
traverseForIndex(d[], fragment.rst)
preparePass2(d.sharedState, firstRst)
# add anchors to overload groups before RST resolution
for k in TSymKind:
@@ -1497,25 +1362,13 @@ proc finishGenerateDoc*(d: var PDoc) =
let refn = overloadGroupName(plainName, k)
let tooltip = "$1 ($2 overloads)" % [
k.toHumanStr & " " & plainName, $overloadChoices.len]
let name = nimIdentBackticksNormalize(plainName)
# save overload group to ``.idx``
let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').
changeFileExt(HtmlExt).string
setIndexTerm(d[], ieNimGroup, htmlFile = external, id = refn,
term = name, linkTitle = k.toHumanStr,
linkDesc = "", line = overloadChoices[0].info.line.int)
if optGenIndexOnly in d.conf.globalOptions: continue
addAnchorNim(d.sharedState, external=false, refn, tooltip,
addAnchorNim(d.sharedState, refn, tooltip,
LangSymbol(symKind: k.toHumanStr,
name: name,
name: nimIdentBackticksNormalize(plainName),
isGroup: true),
priority = symbolPriority(k),
# select index `0` just to have any meaningful warning:
info = overloadChoices[0].info,
module = addRstFileIndex(d, FileIndex d.module.position))
if optGenIndexOnly in d.conf.globalOptions:
return
info = overloadChoices[0].info)
# Finalize fragments of ``.nim`` or ``.rst`` file
proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) =
@@ -1535,7 +1388,7 @@ proc finishGenerateDoc*(d: var PDoc) =
overloadChoices.sort(cmp)
var nameContent = ""
for item in overloadChoices:
var itemDesc: string = ""
var itemDesc: string
renderItemPre(d, item.descRst, itemDesc)
nameContent.add(
getConfigVar(d.conf, "doc.item") % (
@@ -1561,20 +1414,17 @@ proc finishGenerateDoc*(d: var PDoc) =
for i, entry in d.jEntriesPre:
if entry.rst != nil:
let resolved = resolveSubs(d.sharedState, entry.rst)
var str: string = ""
var str: string
renderRstToOut(d[], resolved, str)
entry.json[entry.rstField] = %str
d.jEntriesPre[i].rst = nil
d.jEntriesFinal.add entry.json # generates docs
setIndexTitle(d, useMetaTitle = d.standaloneDoc)
completePass2(d.sharedState)
proc add(d: PDoc; j: JsonItem) =
if j.json != nil or j.rst != nil: d.jEntriesPre.add j
proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool = true) =
proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) =
case n.kind
of nkPragma:
let doctypeNode = findPragma(n, wDoctype)
@@ -1606,18 +1456,18 @@ proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool =
if n[i].kind != nkCommentStmt:
# order is always 'type var let const':
d.add genJsonItem(d, n[i], n[i][0],
succ(skType, ord(n.kind)-ord(nkTypeSection)), optShowNonExportedFields in config.globalOptions)
succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList:
for i in 0..<n.len:
generateJson(d, n[i], config, includeComments)
generateJson(d, n[i], includeComments)
of nkWhenStmt:
# generate documentation for the first branch only:
if not checkForFalse(n[0][0]):
generateJson(d, lastSon(n[0]), config, includeComments)
generateJson(d, lastSon(n[0]), includeComments)
else: discard
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
result = getNameEsc(d, nameNode) & "\n"
result = getName(d, nameNode) & "\n"
proc generateTags*(d: PDoc, n: PNode, r: var string) =
case n.kind
@@ -1676,7 +1526,7 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) =
for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase):
var overloadChoices = d.tocTable[kind][plainName]
overloadChoices.sort(cmp)
var content: string = ""
var content: string
for item in overloadChoices:
content.add item.content
d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [
@@ -1707,7 +1557,7 @@ proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile):
proc genOutFile(d: PDoc, groupedToc = false): string =
var
code, content: string = ""
code, content: string
title = ""
var j = 0
var toc = ""
@@ -1724,7 +1574,13 @@ proc genOutFile(d: PDoc, groupedToc = false): string =
# Extract the title. Non API modules generate an entry in the index table.
if d.meta[metaTitle].len != 0:
title = d.meta[metaTitle]
let external = AbsoluteFile(d.destFile)
.relativeTo(d.conf.outDir, '/')
.changeFileExt(HtmlExt)
.string
setIndexTerm(d[], external, "", title)
else:
# Modules get an automatic title for the HTML, but no entry in the index.
title = canonicalImport(d.conf, AbsoluteFile d.filename)
title = esc(d.target, title)
var subtitle = ""
@@ -1758,22 +1614,16 @@ proc genOutFile(d: PDoc, groupedToc = false): string =
"moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(),
"content", content, "author", d.meta[metaAuthor],
"version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics,
"deprecationMsg", d.modDeprecationMsg, "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
"deprecationMsg", d.modDeprecationMsg]
else:
code = content
result = code
proc indexFile(d: PDoc): AbsoluteFile =
let dir = d.conf.outDir
result = dir / changeFileExt(presentationPath(d.conf,
AbsoluteFile d.filename),
IndexExt)
let (finalDir, _, _) = result.string.splitFile
createDir(finalDir)
proc generateIndex*(d: PDoc) =
if optGenIndex in d.conf.globalOptions:
let dest = indexFile(d)
let dir = d.conf.outDir
createDir(dir)
let dest = dir / changeFileExt(presentationPath(d.conf, AbsoluteFile d.filename), IndexExt)
writeIndexFile(d[], dest.string)
proc updateOutfile(d: PDoc, outfile: AbsoluteFile) =
@@ -1784,9 +1634,6 @@ proc updateOutfile(d: PDoc, outfile: AbsoluteFile) =
d.conf.outFile = splitPath(d.conf.outFile.string)[1].RelativeFile
proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) =
if optGenIndexOnly in d.conf.globalOptions:
d.conf.outFile = indexFile(d).relativeTo(d.conf.outDir) # just for display
return
runAllExamples(d)
var content = genOutFile(d, groupedToc)
if optStdout in d.conf.globalOptions:
@@ -1816,7 +1663,7 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) =
proc writeOutputJson*(d: PDoc, useWarning = false) =
runAllExamples(d)
var modDesc: string = ""
var modDesc: string
for desc in d.modDescFinal:
modDesc &= desc
let content = %*{"orig": d.filename,
@@ -1824,11 +1671,11 @@ proc writeOutputJson*(d: PDoc, useWarning = false) =
"moduleDescription": modDesc,
"entries": d.jEntriesFinal}
if optStdout in d.conf.globalOptions:
writeLine(stdout, $content)
write(stdout, $content)
else:
let dir = d.destFile.splitFile.dir
createDir(dir)
var f: File = default(File)
var f: File
if open(f, d.destFile, fmWrite):
write(f, $content)
close(f)
@@ -1851,7 +1698,7 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
var ast = parseFile(conf.projectMainIdx, cache, conf)
if ast == nil: return
var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
generateDoc(d, ast, ast, conf)
generateDoc(d, ast, ast)
finishGenerateDoc(d)
writeOutput(d)
generateIndex(d)
@@ -1862,16 +1709,13 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
var filen = addFileExt(filename, "txt")
var d = newDocumentor(filen, cache, conf, outExt, standaloneDoc = true,
preferMarkdown = preferMarkdown, hasToc = false)
try:
let rst = parseRst(readFile(filen.string),
line=LineRstInit, column=ColRstInit,
conf, d.sharedState)
d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
finishGenerateDoc(d)
writeOutput(d)
generateIndex(d)
except ERecoverableError:
discard "already reported the error"
let rst = parseRst(readFile(filen.string),
line=LineRstInit, column=ColRstInit,
conf, d.sharedState)
d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
finishGenerateDoc(d)
writeOutput(d)
generateIndex(d)
proc commandRst2Html*(cache: IdentCache, conf: ConfigRef,
preferMarkdown=false) =
@@ -1890,7 +1734,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
status: int; content: string) {.gcsafe.} =
localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
warnUser, "the ':test:' attribute is not supported by this backend")
generateJson(d, ast, conf)
generateJson(d, ast)
finishGenerateDoc(d)
let json = d.jEntriesFinal
let content = pretty(json)
@@ -1900,12 +1744,9 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
else:
#echo getOutFile(gProjectFull, JsonExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
conf.outFile = filename.relativeTo(conf.outDir)
let dir = filename.splitFile.dir
createDir(dir)
try:
writeFile(filename, content)
except IOError:
except:
rawMessage(conf, errCannotOpenFile, filename.string)
proc commandTags*(cache: IdentCache, conf: ConfigRef) =
@@ -1923,18 +1764,14 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
if optStdout in d.conf.globalOptions:
write(stdout, content)
else:
#echo getOutFile(gProjectFull, TagsExt)
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
conf.outFile = filename.relativeTo(conf.outDir)
let dir = filename.splitFile.dir
createDir(dir)
try:
writeFile(filename, content)
except IOError:
except:
rawMessage(conf, errCannotOpenFile, filename.string)
proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
if optGenIndexOnly in conf.globalOptions:
return
var content = mergeIndexes(dir)
var outFile = outFile
@@ -1947,12 +1784,12 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"")
"title", "Index",
"subtitle", "", "tableofcontents", "", "moduledesc", "",
"date", getDateStr(), "time", getClockStr(),
"content", content, "author", "", "version", "", "analytics", "", "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
"content", content, "author", "", "version", "", "analytics", ""]
# no analytics because context is not available
try:
writeFile(filename, code)
except IOError:
except:
rawMessage(conf, errCannotOpenFile, filename.string)
proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
@@ -1966,5 +1803,5 @@ proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile
try:
writeFile(filename, $body)
except IOError:
except:
rawMessage(conf, errCannotOpenFile, filename.string)

View File

@@ -11,7 +11,7 @@
# semantic checking.
import
options, ast, msgs, docgen, lineinfos, pathutils, packages
options, ast, msgs, passes, docgen, lineinfos, pathutils, packages
from modulegraphs import ModuleGraph, PPassContext
@@ -38,27 +38,25 @@ template closeImpl(body: untyped) {.dirty.} =
except IOError:
discard
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
result = nil
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
closeImpl:
writeOutput(g.doc, useWarning, groupedToc)
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
result = nil
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
closeImpl:
writeOutputJson(g.doc, useWarning)
proc processNode*(c: PPassContext, n: PNode): PNode =
proc processNode(c: PPassContext, n: PNode): PNode =
result = n
var g = PGen(c)
if shouldProcess(g):
generateDoc(g.doc, n, n, g.config)
generateDoc(g.doc, n, n)
proc processNodeJson*(c: PPassContext, n: PNode): PNode =
proc processNodeJson(c: PPassContext, n: PNode): PNode =
result = n
var g = PGen(c)
if shouldProcess(g):
generateJson(g.doc, n, g.config, false)
generateJson(g.doc, n, false)
template myOpenImpl(ext: untyped) {.dirty.} =
var g: PGen
@@ -70,11 +68,20 @@ template myOpenImpl(ext: untyped) {.dirty.} =
g.doc = d
result = g
proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
myOpenImpl(HtmlExt)
proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
myOpenImpl(TexExt)
proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
myOpenImpl(JsonExt)
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
const docgen2TexPass* = makePass(open = myOpenTex, process = processNode,
close = close)
const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
close = closeJson)
proc finishDoc2Pass*(project: string) =
discard

View File

@@ -6,15 +6,15 @@ when defined(nimPreviewSlimSystem):
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
result = newSym(skProc, getIdent(g.cache, "$"), idgen, t.owner, info)
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
dest.typ = t
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
res.typ = getSysType(g, info, tyString)
result.typ = newType(tyProc, idgen, t.owner)
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
result.typ.n = newNodeI(nkFormalParams, info)
rawAddSon(result.typ, res.typ)
result.typ.n.add newNodeI(nkEffectList, info)
@@ -33,10 +33,6 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
#newIntTypeNode(nkIntLit, field.position, t)
# safety branch for invalid data:
caseStmt.add newTree(nkElse,
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
newStrNode("", info))))
body.add(caseStmt)
@@ -47,8 +43,8 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
n[bodyPos] = body
n[resultPos] = newSymNode(res)
result.ast = n
incl result.flagsImpl, {sfFromGeneric, sfNeverRaises}
setHookDisamb(g, result, "$enumtostr", t)
incl result.flags, sfFromGeneric
incl result.flags, sfNeverRaises
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
case obj.kind
@@ -60,27 +56,26 @@ proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
result = obj
else:
result = nil
for x in obj:
result = searchObjCaseImpl(x, field)
if result != nil: break
proc searchObjCase(t: PType; field: PSym): PNode =
result = searchObjCaseImpl(t.n, field)
if result == nil and t.baseClass != nil:
result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
if result == nil and t.len > 0:
result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
doAssert result != nil
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), idgen, t.owner, info)
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
dest.typ = field.typ
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
res.typ = getSysType(g, info, tyUInt8)
result.typ = newType(tyProc, idgen, t.owner)
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
result.typ.n = newNodeI(nkFormalParams, info)
rawAddSon(result.typ, res.typ)
result.typ.n.add newNodeI(nkEffectList, info)
@@ -110,4 +105,5 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
n[bodyPos] = body
n[resultPos] = newSymNode(res)
result.ast = n
incl result.flagsImpl, {sfFromGeneric, sfNeverRaises}
incl result.flags, sfFromGeneric
incl result.flags, sfNeverRaises

View File

@@ -10,8 +10,7 @@
## This module contains support code for new-styled error
## handling via an `nkError` node kind.
import ast, renderer, options, types
import std/strutils
import ast, renderer, options, strutils, types
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -41,8 +40,7 @@ proc newError*(wrongNode: PNode; k: ErrorKind; args: varargs[PNode]): PNode =
let innerError = errorSubNode(wrongNode)
if innerError != nil:
return innerError
var idgen = idGeneratorForPackage(-1'i32)
result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
result.add wrongNode
result.add newIntNode(nkIntLit, ord(k))
for a in args: result.add a
@@ -52,8 +50,7 @@ proc newError*(wrongNode: PNode; msg: string): PNode =
let innerError = errorSubNode(wrongNode)
if innerError != nil:
return innerError
var idgen = idGeneratorForPackage(-1'i32)
result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
result.add wrongNode
result.add newIntNode(nkIntLit, ord(CustomError))
result.add newStrNode(msg, wrongNode.info)

View File

@@ -9,11 +9,9 @@
## This file implements the FFI part of the evaluator for Nim code.
import ast, types, options, msgs, lineinfos
from std/os import getAppFilename
import libffi/libffi
import std/[tables, dynlib]
import ast, types, options, tables, dynlib, msgs, lineinfos
from os import getAppFilename
import pkg/libffi
when defined(windows):
const libcDll = "msvcrt.dll"
@@ -39,10 +37,9 @@ else:
var gExeHandle = loadLib()
proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
result = nil
if dll in cache:
return cache[dll]
var libs: seq[string] = @[]
var libs: seq[string]
libCandidates(dll, libs)
for c in libs:
result = loadLib(c)
@@ -64,7 +61,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
let lib = sym.annex
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
globalError(conf, sym.info, "dynlib needs to be a string lit")
var theAddr: pointer = nil
var theAddr: pointer
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
# first try this exe itself:
@@ -80,7 +77,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
theAddr = dllhandle.symAddr(name.cstring)
if theAddr.isNil: globalError(conf, sym.info,
"cannot import symbol: " & name & " from " & libPathMsg)
result.intVal = cast[int](theAddr)
result.intVal = cast[ByteAddress](theAddr)
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
if t == nil: return addr libffi.type_void
@@ -99,7 +96,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
result = addr libffi.type_pointer
of tyDistinct, tyAlias, tySink:
result = mapType(conf, t.skipModifier)
result = mapType(conf, t[0])
else:
result = nil
# too risky:
@@ -111,13 +108,12 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI
of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
result = default(TABI)
globalError(conf, info, "cannot map calling convention to FFI")
template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
template `+!`(x, y: untyped): untyped =
cast[pointer](cast[int](x) + y)
cast[pointer](cast[ByteAddress](x) + y)
proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
## computes the size of the blob
@@ -126,18 +122,16 @@ proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(conf, v[0], typ.elementType)
result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = packSize(conf, v, typ.skipModifier)
result = packSize(conf, v, typ[0])
of tyArray:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
# we use the real length here instead
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
elif v.len != 0:
result = v.len * packSize(conf, v[0], typ.elementType)
else:
result = 0
result = v.len * packSize(conf, v[0], typ[1])
else:
result = getSize(conf, typ).int
@@ -146,7 +140,6 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
case n.kind
of nkRecList:
result = nil
for i in 0..<n.len:
result = getField(conf, n[i], position)
if result != nil: return
@@ -161,8 +154,7 @@ proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
else: internalError(conf, n.info, "getField(record case branch)")
of nkSym:
if n.sym.position == position: result = n.sym
else: result = nil
else: result = nil
else: discard
proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
@@ -234,19 +226,19 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
packRecCheck = 0
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(conf, v[0], typ.elementType, res +! sizeof(pointer))
pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
dec packRecCheck
awr(pointer, res +! sizeof(pointer))
of tyArray:
let baseSize = getSize(conf, typ.elementType)
let baseSize = getSize(conf, typ[1])
for i in 0..<v.len:
pack(conf, v[i], typ.elementType, res +! i * baseSize)
pack(conf, v[i], typ[1], res +! i * baseSize)
of tyObject, tyTuple:
packObject(conf, v, typ, res)
of tyNil:
discard
of tyDistinct, tyGenericInst, tyAlias, tySink:
pack(conf, v, typ.skipModifier, res)
pack(conf, v, typ[0], res)
else:
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
@@ -304,9 +296,9 @@ proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
result = n
if result.kind != nkBracket:
globalError(conf, n.info, "cannot map value from FFI")
let baseSize = getSize(conf, typ.elementType)
let baseSize = getSize(conf, typ[1])
for i in 0..<result.len:
result[i] = unpack(conf, x +! i * baseSize, typ.elementType, result[i])
result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
proc canonNodeKind(k: TNodeKind): TNodeKind =
case k
@@ -364,7 +356,6 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
else:
result = nil
globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
of tyFloat: awf(nkFloatLit, rd(float, x))
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
@@ -378,19 +369,18 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
# in their unboxed representation so nothing it to be unpacked:
result = n
else:
awi(nkPtrLit, cast[int](p))
awi(nkPtrLit, cast[ByteAddress](p))
of tyPtr, tyRef, tyVar, tyLent:
let p = rd(pointer, x)
if p.isNil:
setNil()
elif n == nil or n.kind == nkPtrLit:
awi(nkPtrLit, cast[int](p))
awi(nkPtrLit, cast[ByteAddress](p))
elif n != nil and n.len == 1:
internalAssert(conf, n.kind == nkRefTy)
n[0] = unpack(conf, p, typ.elementType, n[0])
n[0] = unpack(conf, p, typ.lastSon, n[0])
result = n
else:
result = nil
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
of tyObject, tyTuple:
result = unpackObject(conf, x, typ, n)
@@ -405,10 +395,9 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
of tyNil:
setNil()
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = unpack(conf, x, typ.skipModifier, n)
result = unpack(conf, x, typ.lastSon, n)
else:
# XXX what to do with 'array' here?
result = nil
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
@@ -434,8 +423,8 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
internalAssert conf, call[0].kind == nkPtrLit
var cif: TCif = default(TCif)
var sig: ParamList = default(ParamList)
var cif: TCif
var sig: ParamList
# use the arguments' types for varargs support:
for i in 1..<call.len:
sig[i-1] = mapType(conf, call[i].typ)
@@ -444,24 +433,24 @@ proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
let typ = call[0].typ
if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
mapType(conf, typ.returnType), sig) != OK:
mapType(conf, typ[0]), sig) != OK:
globalError(conf, call.info, "error in FFI call")
var args: ArgList = default(ArgList)
var args: ArgList
let fn = cast[pointer](call[0].intVal)
for i in 1..<call.len:
var t = call[i].typ
args[i-1] = alloc0(packSize(conf, call[i], t))
pack(conf, call[i], t, args[i-1])
let retVal = if isEmptyType(typ.returnType): pointer(nil)
else: alloc(getSize(conf, typ.returnType).int)
let retVal = if isEmptyType(typ[0]): pointer(nil)
else: alloc(getSize(conf, typ[0]).int)
libffi.call(cif, fn, retVal, args)
if retVal.isNil:
result = newNode(nkEmpty)
else:
result = unpack(conf, retVal, typ.returnType, nil)
result = unpack(conf, retVal, typ[0], nil)
result.info = call.info
if retVal != nil: dealloc retVal
@@ -474,8 +463,8 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
info: TLineInfo): PNode =
internalAssert conf, fn.kind == nkPtrLit
var cif: TCif = default(TCif)
var sig: ParamList = default(ParamList)
var cif: TCif
var sig: ParamList
for i in 0..len-1:
var aTyp = args[i+start].typ
if aTyp.isNil:
@@ -489,7 +478,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
mapType(conf, fntyp[0]), sig) != OK:
globalError(conf, info, "error in FFI call")
var cargs: ArgList = default(ArgList)
var cargs: ArgList
let fn = cast[pointer](fn.intVal)
for i in 0..len-1:
let t = args[i+start].typ

View File

@@ -9,16 +9,16 @@
## Template evaluation engine. Now hygienic.
import options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
import std/strutils
import
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
type
TemplCtx = object
owner, genSymOwner: PSym
instLines: bool # use the instantiation lines numbers
isDeclarative: bool
mapping: SymMapping # every gensym'ed symbol needs to be mapped to some
# new symbol
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
# new symbol
config: ConfigRef
ic: IdentCache
instID: int
@@ -33,19 +33,6 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
let x = param
if x.kind == nkArgList:
for y in items(x): result.add(y)
elif nfDefaultRefsParam in x.flags:
# value of default param needs to be evaluated like template body
# if it contains other template params
var res: PNode
if isAtom(x):
res = newNodeI(nkPar, x.info)
evalTemplateAux(x, actual, c, res)
if res.len == 1: res = res[0]
else:
res = copyNode(x)
for i in 0..<x.safeLen:
evalTemplateAux(x[i], actual, c, res)
result.add res
else:
result.add copyTree(x)
@@ -57,19 +44,18 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
handleParam actual[s.position]
elif (s.owner != nil) and (s.kind == skGenericParam or
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
handleParam actual[s.owner.typ.signatureLen + s.position - 1]
handleParam actual[s.owner.typ.len + s.position - 1]
else:
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
var x = idTableGet(c.mapping, s)
var x = PSym(idTableGet(c.mapping, s))
if x == nil:
x = copySym(s, c.idgen)
x = copySym(s, nextSymId(c.idgen))
# sem'check needs to set the owner properly later, see bug #9476
setOwner(x, nil) # c.genSymOwner
x.owner = nil # c.genSymOwner
#if x.kind == skParam and x.owner.kind == skModule:
# internalAssert c.config, false
idTablePut(c.mapping, s, x)
if sfGenSym in s.flags:
# TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
if c.instLines: actual.info else: templ.info)
else:
@@ -130,7 +116,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
# now that we have working untyped parameters.
genericParams = if fromHlo: 0
else: s.ast[genericParamsPos].len
expectedRegularParams = s.typ.paramsLen
expectedRegularParams = s.typ.len-1
givenRegularParams = totalParams - genericParams
if givenRegularParams < 0: givenRegularParams = 0
@@ -196,14 +182,14 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
# replace each param by the corresponding node:
var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
var ctx = TemplCtx(owner: tmpl,
genSymOwner: genSymOwner,
config: conf,
ic: ic,
mapping: initSymMapping(),
instID: instID[],
idgen: idgen
)
var ctx: TemplCtx
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
ctx.config = conf
ctx.ic = ic
initIdTable(ctx.mapping)
ctx.instID = instID[]
ctx.idgen = idgen
let body = tmpl.ast[bodyPos]
#echo "instantion of ", renderTree(body, {renderIds})

View File

@@ -1,131 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2023 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import lineinfos, ast, types
proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int =
result = 0
for i in 1 ..< obj.len:
for j in 0 .. obj[i].len - 2:
if obj[i][j].kind == nkRange:
let x = getOrdValue(obj[i][j][0])
let y = getOrdValue(obj[i][j][1])
if branch >= x and branch <= y:
return i
elif getOrdValue(obj[i][j]) == branch:
return i
if obj[i].len == 1:
# else branch
return i
return 1
template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t)
proc expandDefault*(t: PType; info: TLineInfo): PNode
proc expandField(s: PSym; info: TLineInfo): PNode =
result = newNodeIT(nkExprColonExpr, info, s.typ)
result.add newSymNode(s)
result.add expandDefault(s.typ, info)
proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
case n.kind
of nkRecList:
for i in 0..<n.len:
expandDefaultN(n[i], info, res)
of nkRecCase:
res.add expandField(n[0].sym, info)
var branch = Zero
let constOrNil = n[0].sym.astdef
if constOrNil != nil:
branch = getOrdValue(constOrNil)
let selectedBranch = caseObjDefaultBranch(n, branch)
let b = lastSon(n[selectedBranch])
expandDefaultN b, info, res
of nkSym:
res.add expandField(n.sym, info)
else:
discard
proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
if t.baseClass != nil:
expandDefaultObj(t.baseClass, info, res)
expandDefaultN(t.n, info, res)
proc expandDefault(t: PType; info: TLineInfo): PNode =
case t.kind
of tyInt: result = newZero(t, info, nkIntLit)
of tyInt8: result = newZero(t, info, nkInt8Lit)
of tyInt16: result = newZero(t, info, nkInt16Lit)
of tyInt32: result = newZero(t, info, nkInt32Lit)
of tyInt64: result = newZero(t, info, nkInt64Lit)
of tyUInt: result = newZero(t, info, nkUIntLit)
of tyUInt8: result = newZero(t, info, nkUInt8Lit)
of tyUInt16: result = newZero(t, info, nkUInt16Lit)
of tyUInt32: result = newZero(t, info, nkUInt32Lit)
of tyUInt64: result = newZero(t, info, nkUInt64Lit)
of tyFloat: result = newZero(t, info, nkFloatLit)
of tyFloat32: result = newZero(t, info, nkFloat32Lit)
of tyFloat64: result = newZero(t, info, nkFloat64Lit)
of tyFloat128: result = newZero(t, info, nkFloat64Lit)
of tyChar: result = newZero(t, info, nkCharLit)
of tyBool: result = newZero(t, info, nkIntLit)
of tyEnum:
# Could use low(T) here to finally fix old language quirks
result = newZero(t, info, nkIntLit)
of tyRange:
# Could use low(T) here to finally fix old language quirks
result = expandDefault(skipModifier t, info)
of tyVoid: result = newZero(t, info, nkEmpty)
of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
result = expandDefault(t.skipModifier, info)
of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
if t.hasElementType:
result = expandDefault(t.skipModifier, info)
else:
result = newZero(t, info, nkEmpty)
of tyFromExpr:
if t.n != nil and t.n.typ != nil:
result = expandDefault(t.n.typ, info)
else:
result = newZero(t, info, nkEmpty)
of tyArray:
result = newZero(t, info, nkBracket)
let n = toInt64(lengthOrd(nil, t))
for i in 0..<n:
result.add expandDefault(t.elementType, info)
of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
result = newZero(t, info, nkNilLit)
of tyVar, tyLent:
let e = t.elementType
if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
# skip the modifier, `var openArray` is a (ptr, len) pair too:
result = expandDefault(e, info)
else:
result = newZero(e, info, nkNilLit)
of tySet:
result = newZero(t, info, nkCurly)
of tyObject:
result = newNodeIT(nkObjConstr, info, t)
result.add newNodeIT(nkType, info, t)
expandDefaultObj(t, info, result)
of tyTuple:
result = newZero(t, info, nkTupleConstr)
for it in t.kids:
result.add expandDefault(it, info)
of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
result = newZero(t, info, nkBracket)
of tyString:
result = newZero(t, info, nkStrLit)
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
result = newZero(t, info, nkEmpty) # bug indicator

View File

@@ -14,14 +14,12 @@
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
import std/[os, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
import std / strutils except addf
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import ../dist/checksums/src/checksums/sha1
import std/syncio
type
TInfoCCProp* = enum # properties of the C compiler:
@@ -33,7 +31,6 @@ type
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
hasDeclspec, # CC has __declspec(X)
hasAttribute, # CC has __attribute__((X))
hasBuiltinUnreachable # CC has __builtin_unreachable
TInfoCCProps* = set[TInfoCCProp]
TInfoCC* = tuple[
name: string, # the short name of the compiler
@@ -63,7 +60,7 @@ type
# Configuration settings for various compilers.
# When adding new compilers, the cmake sources could be a good reference:
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
template compiler(name, settings: untyped): untyped =
proc name: TInfoCC {.compileTime.} = settings
@@ -96,7 +93,7 @@ compiler gcc:
produceAsm: gnuAsmListing,
cppXsupport: "-std=gnu++17 -funsigned-char",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasAttribute, hasBuiltinUnreachable})
hasAttribute})
# GNU C and C++ Compiler
compiler nintendoSwitchGCC:
@@ -123,7 +120,7 @@ compiler nintendoSwitchGCC:
produceAsm: gnuAsmListing,
cppXsupport: "-std=gnu++17 -funsigned-char",
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
hasAttribute, hasBuiltinUnreachable})
hasAttribute})
# LLVM Frontend for GCC/G++
compiler llvmGcc:
@@ -162,11 +159,7 @@ compiler vcc:
linkerExe: "cl",
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
includeCmd: " /I",
# HACK: we call `cl` so we have to pass `/link` for linker options,
# but users may still want to pass arguments to `cl` (see #14221)
# to deal with this, we add `/link` before each `/LIBPATH`,
# the linker ignores extra `/link`s since it's an unrecognized argument
linkDirCmd: " /link /LIBPATH:",
linkDirCmd: " /LIBPATH:",
linkLibCmd: " $1.lib",
debug: " /RTC1 /Z7 ",
pic: "",
@@ -176,22 +169,6 @@ compiler vcc:
cppXsupport: "",
props: {hasCpp, hasAssume, hasDeclspec})
# Nvidia CUDA NVCC Compiler
compiler nvcc:
result = gcc()
result.name = "nvcc"
result.compilerExe = "nvcc"
result.cppCompiler = "nvcc"
result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
# AMD HIPCC Compiler (rocm/cuda)
compiler hipcc:
result = clang()
result.name = "hipcc"
result.compilerExe = "hipcc"
result.cppCompiler = "hipcc"
compiler clangcl:
result = vcc()
result.name = "clang_cl"
@@ -257,8 +234,8 @@ compiler tcc:
linkerExe: "tcc",
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
includeCmd: " -I",
linkDirCmd: " -L",
linkLibCmd: " -l$1",
linkDirCmd: "", # XXX: not supported yet
linkLibCmd: "", # XXX: not supported yet
debug: " -g ",
pic: "",
asmStmtFrmt: "asm($1);$n",
@@ -305,9 +282,7 @@ const
envcc(),
icl(),
icc(),
clangcl(),
hipcc(),
nvcc()]
clangcl()]
hExt* = ".h"
@@ -341,7 +316,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
var fullSuffix = suffix
case conf.backend
of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
of backendC, backendNif: discard
of backendC: discard
of backendInvalid:
# during parsing of cfg files; we don't know the backend yet, no point in
# guessing wrong thing
@@ -353,9 +328,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
platform.OS[conf.target.targetOS].name & '.' &
CC[c].name & fullSuffix
result = getConfigVar(conf, fullCCname)
if existsConfigVar(conf, fullCCname):
result = getConfigVar(conf, fullCCname)
else:
if result.len == 0:
# not overridden for this cross compilation setting?
result = getConfigVar(conf, CC[c].name & fullSuffix)
else:
@@ -474,11 +447,6 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
result = conf.compileOptions
if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
conf.selectedGC == gcRefc:
# bug #10625
addOpt(result, "-fno-omit-frame-pointer")
for option in conf.compileOptionsCmd:
if strutils.find(result, option, 0) < 0:
addOpt(result, option)
@@ -514,10 +482,6 @@ proc vccplatform(conf: ConfigRef): string =
of cpuArm: " --platform:arm"
of cpuAmd64: " --platform:amd64"
else: ""
else:
result = ""
else:
result = ""
proc getLinkOptions(conf: ConfigRef): string =
result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
@@ -564,9 +528,9 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool =
var exe = getConfigVar(conf, conf.cCompiler, ".exe")
if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
# NOTE: should we need the full version, use -dumpfullversion
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except: ("", 1)
if exitCode == 0:
var major: int = 0
var major: int
discard parseInt(s, major)
result = major >= 5
else:
@@ -612,7 +576,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
compilePattern = joinPath(conf.cCompilerPath, exe)
else:
compilePattern = exe
compilePattern = getCompilerExe(conf, c, isCpp)
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
@@ -676,7 +640,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
let currentHash = footprint(conf, cfile)
var f: File = default(File)
var f: File
if open(f, hashFile.string, fmRead):
let oldHash = parseSecureHash(f.readLine())
close(f)
@@ -786,7 +750,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
# https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
# and a bit about the .pdb format in case that is ever needed:
# https://github.com/crosire/blink
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
# http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
if conf.hcrOn and isVSCompatible(conf):
let t = now()
let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
@@ -810,61 +774,7 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
(ose.msg & " " & $ose.errorCode))
raise
proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) =
let (dir, name, _) = splitFile(exefile.string)
let appBundleName = name & ".app"
let appBundlePath = dir / appBundleName
let contentsPath = appBundlePath / "Contents"
let macosPath = contentsPath / "MacOS"
createDir(macosPath)
let bundleExePath = macosPath / name
copyFileWithPermissions(exefile.string, bundleExePath)
let infoPlistPath = contentsPath / "Info.plist"
proc xmlEscape(s: string): string =
result = newStringOfCap(s.len)
for c in items(s):
case c:
of '<': result.add("&lt;")
of '>': result.add("&gt;")
of '&': result.add("&amp;")
of '"': result.add("&quot;")
of '\'': result.add("&apos;")
else:
if ord(c) < 32:
result.add("&#" & $ord(c) & ';')
else:
result.add(c)
let escapedName = xmlEscape(name)
let infoPlistContent = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>$1</string>
<key>CFBundleIdentifier</key>
<string>com.nim.$1</string>
<key>CFBundleName</key>
<string>$1</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>""" % [escapedName]
writeFile(infoPlistPath, infoPlistContent)
removeFile(exefile.string)
rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath)
proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
result = @[]
when defined(macosx):
if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions:
# if needed, add an option to skip or override location
@@ -920,22 +830,10 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
else:
writeFile(linkerArgs, args)
try:
when defined(macosx):
execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs)
else:
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
finally:
removeFile(linkerArgs)
proc linkViaShellScript(conf: ConfigRef; cmd: string) =
let linkerScript = conf.projectName & "_" & "linkerScript.sh"
writeFile(linkerScript, cmd)
let shell = getEnv("SHELL")
try:
execLinkCmd(conf, shell & " " & linkerScript)
finally:
removeFile(linkerScript)
proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
if noAbsolutePaths(conf): f.obj.extractFilename
else: f.obj.string
@@ -947,38 +845,23 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
result = conf.getNimcacheDir / RelativeFile(targetName)
proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
result = ""
if conf.hasHint(hintCC):
if optListCmd in conf.globalOptions or conf.verbosity > 1:
result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd)
else:
result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name)
proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) =
# Prevent linkcmd from exceeding the maximum command line length.
# Windows's command line limit is about 8K (8191 characters) so C compilers on
# Windows support a feature where the command line can be passed via ``@linkcmd``
# to them.
const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000
if linkCmd.len > MaxCmdLen:
when defined(macosx):
linkViaShellScript(conf, linkCmd)
else:
linkViaResponseFile(conf, linkCmd)
else:
execLinkCmd(conf, linkCmd)
proc callCCompiler*(conf: ConfigRef) =
var
linkCmd: string = ""
linkCmd: string
extraCmds: seq[string]
if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
return # speed up that call if only compiling and no script shall be
# generated
#var c = cCompiler
var script: Rope = ""
var cmds: TStringSeq = default(TStringSeq)
var prettyCmds: TStringSeq = default(TStringSeq)
var cmds: TStringSeq
var prettyCmds: TStringSeq
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
for idx, it in conf.toCompile:
@@ -1044,13 +927,16 @@ proc callCCompiler*(conf: ConfigRef) =
linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true)
extraCmds = getExtraCmds(conf, mainOutput)
if optCompileOnly notin conf.globalOptions:
preventLinkCmdMaxCmdLen(conf, linkCmd)
const MaxCmdLen = when defined(windows): 8_000 else: 32_000
if linkCmd.len > MaxCmdLen:
# Windows's command line limit is about 8K (don't laugh...) so C compilers on
# Windows support a feature where the command line can be passed via ``@linkcmd``
# to them.
linkViaResponseFile(conf, linkCmd)
else:
execLinkCmd(conf, linkCmd)
for cmd in extraCmds:
execExternalProgram(conf, cmd, hintExecuting)
# create Mac app bundle for GUI apps on macOS
when defined(macosx):
if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}:
createMacAppBundle(conf, mainOutput)
else:
linkCmd = ""
if optGenScript in conf.globalOptions:
@@ -1066,11 +952,10 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
# works out of the box with `hashMainCompilationParams`.
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
type BuildCache = object
cacheVersion: string
outputFile: string
outputLastModificationTime: string
compile: seq[(string, string)]
link: seq[string]
linkcmd: string
@@ -1084,7 +969,7 @@ type BuildCache = object
depfiles: seq[(string, string)]
nimexe: string
proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
proc writeJsonBuildInstructions*(conf: ConfigRef) =
var linkFiles = collect(for it in conf.externalToLink:
var it = it
if conf.noAbsolutePaths: it = it.extractFilename
@@ -1105,24 +990,17 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
currentDir: getCurrentDir())
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
bcache.cmdline = conf.commandLine
for it in conf.m.fileInfos:
bcache.depfiles = collect(for it in conf.m.fileInfos:
let path = it.fullPath.string
if isAbsolute(path): # TODO: else?
if path in deps:
bcache.depfiles.add (path, deps[path])
else: # backup for configs etc.
bcache.depfiles.add (path, $secureHashFile(path))
(path, $secureHashFile(path)))
bcache.nimexe = hashNimExe()
if fileExists(bcache.outputFile):
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool =
result = false
if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true
var bcache: BuildCache = default(BuildCache)
var bcache: BuildCache
try: bcache.fromJson(jsonFile.string.parseFile)
except IOError, OSError, ValueError:
stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string
@@ -1136,13 +1014,11 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
# xxx optimize by returning false if stdin input was the same
for (file, hash) in bcache.depfiles:
if $secureHashFile(file) != hash: return true
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
return true
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
var bcache: BuildCache = default(BuildCache)
var bcache: BuildCache
try: bcache.fromJson(jsonFile.string.parseFile)
except ValueError, KeyError, JsonKindError:
except:
let e = getCurrentException()
conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
[e.msg, e.getStackTrace(), jsonFile.string]
@@ -1153,18 +1029,16 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
globalError(conf, gCmdLineInfo,
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
[outputCurrent, output, jsonFile.string])
var cmds: TStringSeq = default(TStringSeq)
var prettyCmds: TStringSeq = default(TStringSeq)
var cmds, prettyCmds: TStringSeq
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
for (name, cmd) in bcache.compile:
cmds.add cmd
prettyCmds.add displayProgressCC(conf, name, cmd)
execCmdsInParallel(conf, cmds, prettyCb)
preventLinkCmdMaxCmdLen(conf, bcache.linkcmd)
execLinkCmd(conf, bcache.linkcmd)
for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting)
proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
result = ""
for it in list:
result.addf("--file:r\"$1\"$N", [rope(it.cname.string)])

View File

@@ -10,11 +10,9 @@
# This module implements Nim's standard template filter.
import
llstream, ast, msgs, options,
llstream, strutils, ast, msgs, options,
filters, lineinfos, pathutils
import std/strutils
type
TParseState = enum
psDirective, psTempl
@@ -203,15 +201,17 @@ proc parseLine(p: var TTmplParser) =
proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
call: PNode): PLLStream =
var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
outp: llStreamOpen(""), inp: stdin,
subsChar: charArg(conf, call, "subschar", 1, '$'),
nimDirective: charArg(conf, call, "metachar", 2, '#'),
emit: strArg(conf, call, "emit", 3, "result.add"),
conc: strArg(conf, call, "conc", 4, " & "),
toStr: strArg(conf, call, "tostring", 5, "$"),
x: newStringOfCap(120)
)
var p: TTmplParser
p.config = conf
p.info = newLineInfo(conf, filename, 0, 0)
p.outp = llStreamOpen("")
p.inp = stdin
p.subsChar = charArg(conf, call, "subschar", 1, '$')
p.nimDirective = charArg(conf, call, "metachar", 2, '#')
p.emit = strArg(conf, call, "emit", 3, "result.add")
p.conc = strArg(conf, call, "conc", 4, " & ")
p.toStr = strArg(conf, call, "tostring", 5, "$")
p.x = newStringOfCap(120)
# do not process the first line which contains the directive:
if llStreamReadLine(p.inp, p.x):
inc p.info.line

View File

@@ -10,11 +10,9 @@
# This module implements Nim's simple filters and helpers for filters.
import
llstream, idents, ast, msgs, options,
llstream, idents, strutils, ast, msgs, options,
renderer, pathutils
import std/strutils
proc invalidPragma(conf: ConfigRef; n: PNode) =
localError(conf, n.info,
"'$1' not allowed here" % renderTree(n, {renderNoComments}))
@@ -31,30 +29,23 @@ proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
return n[i]
proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind == nkCharLit: result = chr(int(x.intVal))
else:
result = default(char)
invalidPragma(conf, n)
else: invalidPragma(conf, n)
proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
else:
result = ""
invalidPragma(conf, n)
else: invalidPragma(conf, n)
proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
else:
result = false
invalidPragma(conf, n)
else: invalidPragma(conf, n)
proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
var pattern = strArg(conf, call, "startswith", 1, "")

View File

@@ -9,15 +9,12 @@
## Module that implements ``gorge`` for the compiler.
import msgs, options, lineinfos, pathutils
import std/[os, osproc, streams]
import msgs, std / sha1, os, osproc, streams, options,
lineinfos, pathutils
when defined(nimPreviewSlimSystem):
import std/syncio
import ../dist/checksums/src/checksums/sha1
proc readOutput(p: Process): (string, int) =
result[0] = ""
var output = p.outputStream
@@ -30,11 +27,10 @@ proc readOutput(p: Process): (string, int) =
proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
let workingDir = parentDir(toFullPath(conf, info))
result = ("", 0)
if cache.len > 0:
let h = secureHash(cmd & "\t" & input & "\t" & cache)
let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
var f: File = default(File)
var f: File
if optForceFullMake notin conf.globalOptions and open(f, filename):
result = (f.readAll, 0)
f.close

View File

@@ -46,15 +46,11 @@ proc isLocation(n: PNode): bool = not n.isValue
proc isLet(n: PNode): bool =
if n.kind == nkSym:
if n.sym.kind in {skLet, skConst, skTemp, skForVar}: # guard immutable variables
if n.sym.kind in {skLet, skTemp, skForVar}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind notin {tyVar}:
result = true
else:
result = false
else:
result = false
proc isVar(n: PNode): bool =
n.kind == nkSym and n.sym.kind in {skResult, skVar} and
@@ -140,8 +136,6 @@ proc neg(n: PNode; o: Operators): PNode =
result = a
elif b != nil:
result = b
else:
result = nil
else:
# leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2)
@@ -336,8 +330,6 @@ proc usefulFact(n: PNode; o: Operators): PNode =
result = n
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
result = n
else:
result = nil
of someLe+someLt:
if isLetLocation(n[1], true) or isLetLocation(n[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
@@ -345,18 +337,12 @@ proc usefulFact(n: PNode; o: Operators): PNode =
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
# XXX Rethink this whole idea of 'usefulFact' for semparallel
result = n
else:
result = nil
of mIsNil:
if isLetLocation(n[1], false) or isVar(n[1]):
result = n
else:
result = nil
of someIn:
if isLetLocation(n[1], true):
result = n
else:
result = nil
of mAnd:
let
a = usefulFact(n[1], o)
@@ -370,14 +356,10 @@ proc usefulFact(n: PNode; o: Operators): PNode =
result = a
elif b != nil:
result = b
else:
result = nil
of mNot:
let a = usefulFact(n[1], o)
if a != nil:
result = a.neg(o)
else:
result = nil
of mOr:
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
# with that knowledge...
@@ -394,8 +376,6 @@ proc usefulFact(n: PNode; o: Operators): PNode =
result[1] = a
result[2] = b
result = result.neg(o)
else:
result = nil
elif n.kind == nkSym and n.sym.kind == skLet:
# consider:
# let a = 2 < x
@@ -404,12 +384,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
# We make can easily replace 'a' by '2 < x' here:
if n.sym.astdef != nil:
result = usefulFact(n.sym.astdef, o)
else:
result = nil
elif n.kind == nkStmtListExpr:
result = usefulFact(n.lastSon, o)
else:
result = nil
type
TModel* = object
@@ -475,9 +451,8 @@ proc hasSubTree(n, x: PNode): bool =
of nkEmpty..nkNilLit:
result = n.sameTree(x)
of nkFormalParams:
result = false
discard
else:
result = false
for i in 0..<n.len:
if hasSubTree(n[i], x): return true
@@ -508,8 +483,6 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
proc valuesUnequal(a, b: PNode): bool =
if a.isValue and b.isValue:
result = not sameValue(a, b)
else:
result = false
proc impliesEq(fact, eq: PNode): TImplication =
let (loc, val) = if isLocation(eq[1]): (1, 2) else: (2, 1)
@@ -520,26 +493,16 @@ proc impliesEq(fact, eq: PNode): TImplication =
# this is not correct; consider: a == b; a == 1 --> unknown!
if sameTree(fact[2], eq[val]): result = impYes
elif valuesUnequal(fact[2], eq[val]): result = impNo
else:
result = impUnknown
elif sameTree(fact[2], eq[loc]):
if sameTree(fact[1], eq[val]): result = impYes
elif valuesUnequal(fact[1], eq[val]): result = impNo
else:
result = impUnknown
else:
result = impUnknown
of mInSet:
# remember: mInSet is 'contains' so the set comes first!
if sameTree(fact[2], eq[loc]) and isValue(eq[val]):
if inSet(fact[1], eq[val]): result = impYes
else: result = impNo
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesEq")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesEq")
else: discard
proc leImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -549,19 +512,13 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
var value = newIntNode(c.kind, firstOrd(nil, x.typ))
# don't iterate too often:
if c.intVal - value.intVal < 1000:
var i, pos, neg: int = 0
var i, pos, neg: int
while value.intVal <= c.intVal:
if inSet(aSet, value): inc pos
else: inc neg
inc i; inc value.intVal
if pos == i: result = impYes
elif neg == i: result = impNo
else:
result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
proc geImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -572,23 +529,17 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
let max = lastOrd(nil, x.typ)
# don't iterate too often:
if max - getInt(value) < toInt128(1000):
var i, pos, neg: int = 0
var i, pos, neg: int
while value.intVal <= max:
if inSet(aSet, value): inc pos
else: inc neg
inc i; inc value.intVal
if pos == i: result = impYes
elif neg == i: result = impNo
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
proc compareSets(a, b: PNode): TImplication =
if equalSets(nil, a, b): result = impYes
elif intersectSets(nil, a, b).len == 0: result = impNo
else: result = impUnknown
proc impliesIn(fact, loc, aSet: PNode): TImplication =
case fact[0].sym.magic
@@ -599,32 +550,22 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication =
elif sameTree(fact[2], loc):
if inSet(aSet, fact[1]): result = impYes
else: result = impNo
else:
result = impUnknown
of mInSet:
if sameTree(fact[2], loc):
result = compareSets(fact[1], aSet)
else:
result = impUnknown
of someLe:
if sameTree(fact[1], loc):
result = leImpliesIn(fact[1], fact[2], aSet)
elif sameTree(fact[2], loc):
result = geImpliesIn(fact[2], fact[1], aSet)
else:
result = impUnknown
of someLt:
if sameTree(fact[1], loc):
result = leImpliesIn(fact[1], fact[2].pred, aSet)
elif sameTree(fact[2], loc):
# 4 < x --> 3 <= x
result = geImpliesIn(fact[2], fact[1].pred, aSet)
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesIn")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesIn")
else: discard
proc valueIsNil(n: PNode): TImplication =
if n.kind == nkNilLit: impYes
@@ -636,19 +577,13 @@ proc impliesIsNil(fact, eq: PNode): TImplication =
of mIsNil:
if sameTree(fact[1], eq[1]):
result = impYes
else:
result = impUnknown
of someEq:
if sameTree(fact[1], eq[1]):
result = valueIsNil(fact[2].skipConv)
elif sameTree(fact[2], eq[1]):
result = valueIsNil(fact[1].skipConv)
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesIsNil")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesIsNil")
else: discard
proc impliesGe(fact, x, c: PNode): TImplication =
assert isLocation(x)
@@ -659,57 +594,32 @@ proc impliesGe(fact, x, c: PNode): TImplication =
# fact: x = 4; question x >= 56? --> true iff 4 >= 56
if leValue(c, fact[2]): result = impYes
else: result = impNo
else:
result = impUnknown
elif sameTree(fact[2], x):
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1]): result = impYes
else: result = impNo
else:
result = impUnknown
else:
result = impUnknown
of someLt:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x < 4; question N <= x? --> false iff N <= 4
if leValue(fact[2], c): result = impNo
else: result = impUnknown
# fact: x < 4; question 2 <= x? --> we don't know
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3
if isValue(fact[1]) and isValue(c):
if leValue(c.pred, fact[1]): result = impYes
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of someLe:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x <= 4; question x >= 56? --> false iff 4 <= 56
if leValue(fact[2], c): result = impNo
# fact: x <= 4; question x >= 2? --> we don't know
else:
result = impUnknown
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1]): result = impYes
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesGe")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesGe")
else: discard
proc impliesLe(fact, x, c: PNode): TImplication =
if not isLocation(x):
@@ -724,59 +634,35 @@ proc impliesLe(fact, x, c: PNode): TImplication =
# fact: x = 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact[2], c): result = impYes
else: result = impNo
else:
result = impUnknown
elif sameTree(fact[2], x):
if isValue(fact[1]) and isValue(c):
if leValue(fact[1], c): result = impYes
else: result = impNo
else:
result = impUnknown
else:
result = impUnknown
of someLt:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x < 4; question x <= N? --> true iff N-1 <= 4
if leValue(fact[2], c.pred): result = impYes
else:
result = impUnknown
# fact: x < 4; question x <= 2? --> we don't know
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1]): result = impNo
else: result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of someLe:
if sameTree(fact[1], x):
if isValue(fact[2]) and isValue(c):
# fact: x <= 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact[2], c): result = impYes
else: result = impUnknown
# fact: x <= 4; question x <= 2? --> we don't know
else:
result = impUnknown
elif sameTree(fact[2], x):
# fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3
if isValue(fact[1]) and isValue(c):
if leValue(c, fact[1].pred): result = impNo
else:result = impUnknown
else:
result = impUnknown
else:
result = impUnknown
of mNot, mOr, mAnd:
result = impUnknown
assert(false, "impliesLe")
else: result = impUnknown
of mNot, mOr, mAnd: assert(false, "impliesLe")
else: discard
proc impliesLt(fact, x, c: PNode): TImplication =
# x < 3 same as x <= 2:
@@ -788,8 +674,6 @@ proc impliesLt(fact, x, c: PNode): TImplication =
let q = x.pred
if q != x:
result = impliesLe(fact, q, c)
else:
result = impUnknown
proc `~`(x: TImplication): TImplication =
case x
@@ -841,7 +725,6 @@ proc factImplies(fact, prop: PNode): TImplication =
proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds
result = impUnknown
for f in facts.s:
# facts can be invalidated, in which case they are 'nil':
if not f.isNil:
@@ -870,7 +753,7 @@ template isSub(x): untyped = x.getMagic in someSub
template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
template isIntVal(x, y): untyped = x.intVal == y
import std/macros
import macros
macro `=~`(x: PNode, pat: untyped): bool =
proc m(x, pat, conds: NimNode) =
@@ -1017,7 +900,6 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
# now check for inferrable facts: a <= b and b <= c implies a <= c
result = impUnknown
for i in 0..m.s.high:
let fact = m.s[i]
if fact != nil and fact.getMagic in someLe:
@@ -1063,7 +945,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
let b = fact[2]
if a.kind == nkSym: replacements.add((a,b))
else: replacements.add((b,a))
var m = TModel()
var m: TModel
var a = aa
var b = bb
if replacements.len > 0:
@@ -1098,8 +980,8 @@ proc addFactLt*(m: var TModel; a, b: PNode) =
addFactLe(m, a, bb)
proc settype(n: PNode): PType =
var idgen = idGeneratorForPackage(-1'i32)
result = newType(tySet, idgen, n.typ.owner)
result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
var idgen: IdGenerator
addSonSkipIntLit(result, n.typ, idgen)
proc buildOf(it, loc: PNode; o: Operators): PNode =
@@ -1165,12 +1047,8 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
if check[1].kind == nkCurly:
result = copyTree(check)
if access.kind == nkDotExpr:
# change the access to the discriminator field access
var a = copyTree(access)
# set field name to discriminator field name
a[1] = check[2]
# set discriminator field type: important for `neg`
a.typ = check[2].typ
result[2] = a
# 'access.kind != nkDotExpr' can happen for object constructors
# which we don't check yet

View File

@@ -10,18 +10,19 @@
# This include implements the high level optimization pass.
# included from sem.nim
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode
when defined(nimPreviewSlimSystem):
import std/assertions
proc hlo(c: PContext, n: PNode): PNode
proc evalPattern(c: PContext, n, orig: PNode): PNode =
internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
# we need to ensure that the resulting AST is semchecked. However, it's
# awful to semcheck before macro invocation, so we don't and treat
# templates and macros as immediate in this context.
var rule: string =
if c.config.hasHint(hintPattern):
renderTree(n, {renderNoComments})
else:
""
var rule: string
if c.config.hasHint(hintPattern):
rule = renderTree(n, {renderNoComments})
let s = n[0].sym
case s.kind
of skMacro:
@@ -61,11 +62,10 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
# activate this pattern again:
c.patterns[i] = pattern
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
proc hlo(c: PContext, n: PNode): PNode =
inc(c.hloLoopDetector)
# simply stop and do not perform any further transformations:
if loopDetector > 300:
message(c.config, n.info, warnUser, "term rewrite macro instantiation too nested")
return n
if c.hloLoopDetector > 300: return n
case n.kind
of nkMacroDef, nkTemplateDef, procDefs:
# already processed (special cases in semstmts.nim)
@@ -73,7 +73,7 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
else:
if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
n[0].kind == nkSym and
{sfGlobal, sfPure} <= n[0].sym.flags:
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
# do not optimize 'var g {.global} = re(...)' again!
return n
result = applyPatterns(c, n)
@@ -81,7 +81,7 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
# no optimization applied, try subtrees:
for i in 0..<result.safeLen:
let a = result[i]
let h = hlo(c, a, loopDetector)
let h = hlo(c, a)
if h != a: result[i] = h
else:
# perform type checking, so that the replacement still fits:
@@ -91,15 +91,17 @@ proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
result = fitNode(c, n.typ, result, n.info)
# optimization has been applied so check again:
result = commonOptimizations(c.graph, c.idgen, c.module, result)
result = hlo(c, result, loopDetector + 1)
result = hlo(c, result)
result = commonOptimizations(c.graph, c.idgen, c.module, result)
proc hloBody(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
result = hlo(c, n, 0)
c.hloLoopDetector = 0
result = hlo(c, n)
proc hloStmt(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
result = hlo(c, n, 0)
c.hloLoopDetector = 0
result = hlo(c, n)

171
compiler/ic/bitabs.nim Normal file
View File

@@ -0,0 +1,171 @@
## A BiTable is a table that can be seen as an optimized pair
## of `(Table[LitId, Val], Table[Val, LitId])`.
import hashes, rodfiles
when defined(nimPreviewSlimSystem):
import std/assertions
type
LitId* = distinct uint32
BiTable*[T] = object
vals: seq[T] # indexed by LitId
keys: seq[LitId] # indexed by hash(val)
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash
template maxHash(t): untyped = high(t.keys)
template isFilled(x: LitId): bool = x.uint32 > 0'u32
proc `$`*(x: LitId): string {.borrow.}
proc `<`*(x, y: LitId): bool {.borrow.}
proc `<=`*(x, y: LitId): bool {.borrow.}
proc `==`*(x, y: LitId): bool {.borrow.}
proc hash*(x: LitId): Hash {.borrow.}
proc len*[T](t: BiTable[T]): int = t.vals.len
proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
const
idStart = 256 ##
## Ids do not start with 0 but with this value. The IR needs it.
## TODO: explain why
template idToIdx(x: LitId): int = x.int - idStart
proc hasLitId*[T](t: BiTable[T]; x: LitId): bool =
let idx = idToIdx(x)
result = idx >= 0 and idx < t.vals.len
proc enlarge[T](t: var BiTable[T]) =
var n: seq[LitId]
newSeq(n, len(t.keys) * 2)
swap(t.keys, n)
for i in 0..high(n):
let eh = n[i]
if isFilled(eh):
var j = hash(t.vals[idToIdx eh]) and maxHash(t)
while isFilled(t.keys[j]):
j = nextTry(j, maxHash(t))
t.keys[j] = move n[i]
proc getKeyId*[T](t: BiTable[T]; v: T): LitId =
let origH = hash(v)
var h = origH and maxHash(t)
if t.keys.len != 0:
while true:
let litId = t.keys[h]
if not isFilled(litId): break
if t.vals[idToIdx t.keys[h]] == v: return litId
h = nextTry(h, maxHash(t))
return LitId(0)
proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
let origH = hash(v)
var h = origH and maxHash(t)
if t.keys.len != 0:
while true:
let litId = t.keys[h]
if not isFilled(litId): break
if t.vals[idToIdx t.keys[h]] == v: return litId
h = nextTry(h, maxHash(t))
# not found, we need to insert it:
if mustRehash(t.keys.len, t.vals.len):
enlarge(t)
# recompute where to insert:
h = origH and maxHash(t)
while true:
let litId = t.keys[h]
if not isFilled(litId): break
h = nextTry(h, maxHash(t))
else:
setLen(t.keys, 16)
h = origH and maxHash(t)
result = LitId(t.vals.len + idStart)
t.keys[h] = result
t.vals.add v
proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} =
let idx = idToIdx litId
assert idx < t.vals.len
result = t.vals[idx]
proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} =
let idx = idToIdx litId
assert idx < t.vals.len
result = t.vals[idx]
proc hash*[T](t: BiTable[T]): Hash =
## as the keys are hashes of the values, we simply use them instead
var h: Hash = 0
for i, n in pairs t.keys:
h = h !& hash((i, n))
result = !$h
proc store*[T](f: var RodFile; t: BiTable[T]) =
storeSeq(f, t.vals)
storeSeq(f, t.keys)
proc load*[T](f: var RodFile; t: var BiTable[T]) =
loadSeq(f, t.vals)
loadSeq(f, t.keys)
when isMainModule:
var t: BiTable[string]
echo getOrIncl(t, "hello")
echo getOrIncl(t, "hello")
echo getOrIncl(t, "hello3")
echo getOrIncl(t, "hello4")
echo getOrIncl(t, "helloasfasdfdsa")
echo getOrIncl(t, "hello")
echo getKeyId(t, "hello")
echo getKeyId(t, "none")
for i in 0 ..< 100_000:
discard t.getOrIncl($i & "___" & $i)
for i in 0 ..< 100_000:
assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
echo "begin"
echo t.vals.len
echo t.vals[0]
echo t.vals[1004]
echo "middle"
var tf: BiTable[float]
discard tf.getOrIncl(0.4)
discard tf.getOrIncl(16.4)
discard tf.getOrIncl(32.4)
echo getKeyId(tf, 32.4)
var f2 = open("testblah.bin", fmWrite)
echo store(f2, tf)
f2.close
var f1 = open("testblah.bin", fmRead)
var t2: BiTable[float]
echo f1.load(t2)
echo t2.vals.len
echo getKeyId(t2, 32.4)
echo "end"
f1.close

178
compiler/ic/cbackend.nim Normal file
View File

@@ -0,0 +1,178 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## New entry point into our C/C++ code generator. Ideally
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
## to work on packed trees directly and produce the C code as an AST which can
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
## this code. So instead we wrap the existing cgen.nim and its friends so that
## we call directly into the existing code generation logic but avoiding the
## naive, outdated `passes` design. Thus you will see some
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
## also doing cross-module dependency tracking and DCE that we don't need
## anymore. DCE is now done as prepass over the entire packed module graph.
import std/packedsets, algorithm, tables
when defined(nimPreviewSlimSystem):
import std/assertions
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
pathutils, extccomp, msgs, modulepaths]
import packed_ast, ic, dce, rodfiles
proc unpackTree(g: ModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
var decoder = initPackedDecoder(g.config, g.cache)
result = loadNodes(decoder, g.packed, thisModule, tree, n)
proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
if g.backend == nil:
g.backend = cgendata.newModuleList(g)
assert g.backend != nil
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
bmod.idgen = idgenFromLoadedModule(m)
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
var bmod = BModuleList(g.backend).modules[m.module.position]
assert bmod != nil
bmod.flags.incl useAliveDataFromDce
bmod.alive = move alive[m.module.position]
for p in allNodes(m.fromDisk.topLevel):
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
cgen.genTopLevelStmt(bmod, n)
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
for x in mitems(m.fromDisk.emittedTypeInfo):
#echo "found type ", x, " for file ", int(origin)
g.emittedTypeInfo[x] = origin
proc addFileToLink(config: ConfigRef; m: PSym) =
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
let ext =
if config.backend == backendCpp: ".nim.cpp"
elif config.backend == backendObjc: ".nim.m"
else: ".nim.c"
let cfile = changeFileExt(completeCfilePath(config,
mangleModuleName(config, filename).AbsoluteFile), ext)
let objFile = completeCfilePath(config, toObjFile(config, cfile))
if fileExists(objFile):
var cf = Cfile(nimname: m.name.s, cname: cfile,
obj: objFile,
flags: {CfileFlag.Cached})
addFileToCompile(config, cf)
when defined(debugDce):
import os, std/packedsets
proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
var f = rodfiles.create(asymFile.string)
f.storeHeader()
f.storeSection aliveSymsSection
f.storeSeq(s)
close f
template prepare {.dirty.} =
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
var s = newSeqOfCap[int32](alive[position].len)
for a in items(alive[position]): s.add int32(a)
sort(s)
proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
prepare()
storeAliveSymsImpl(asymFile, s)
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
prepare()
var f2 = rodfiles.open(asymFile.string)
f2.loadHeader()
f2.loadSection aliveSymsSection
var oldData: seq[int32]
f2.loadSeq(oldData)
f2.close
if f2.err == ok and oldData == s:
result = false
else:
when defined(debugDce):
let oldAsSet = toPackedSet[int32](oldData)
let newAsSet = toPackedSet[int32](s)
echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len
echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len
#if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
# echo "command failed"
result = true
storeAliveSymsImpl(asymFile, s)
proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) =
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading, stored:
assert false
of storing, outdated:
storeAliveSyms(g.config, g.packed[i].module.position, alive)
generateCodeForModule(g, g.packed[i], alive)
closeRodFile(g, g.packed[i].module)
of loaded:
if g.packed[i].loadedButAliveSetChanged:
generateCodeForModule(g, g.packed[i], alive)
else:
addFileToLink(g.config, g.packed[i].module)
replayTypeInfo(g, g.packed[i], FileIndex(i))
if g.backend == nil:
g.backend = cgendata.newModuleList(g)
registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags)
proc generateCode*(g: ModuleGraph) =
## The single entry point, generate C(++) code for the entire
## Nim program aka `ModuleGraph`.
resetForBackend(g)
var alive = computeAliveSyms(g.packed, g.config)
when false:
for i in 0..high(g.packed):
echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
# First pass: Setup all the backend modules for all the modules that have
# changed:
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading, stored:
assert false
of storing, outdated:
setupBackendModule(g, g.packed[i])
of loaded:
# Even though this module didn't change, DCE might trigger a change.
# Consider this case: Module A uses symbol S from B and B does not use
# S itself. A is then edited not to use S either. Thus we have to
# recompile B in order to remove S from the final result.
if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
g.packed[i].loadedButAliveSetChanged = true
setupBackendModule(g, g.packed[i])
# Second pass: Code generation.
let mainModuleIdx = g.config.projectMainIdx2.int
# We need to generate the main module last, because only then
# all init procs have been registered:
for i in 0..high(g.packed):
if i != mainModuleIdx:
genPackedModule(g, i, alive)
if mainModuleIdx >= 0:
genPackedModule(g, mainModuleIdx, alive)

165
compiler/ic/dce.nim Normal file
View File

@@ -0,0 +1,165 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Dead code elimination (=DCE) for IC.
import std/[intsets, tables]
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, options, lineinfos, types]
import packed_ast, ic, bitabs
type
AliveSyms* = seq[IntSet]
AliveContext* = object ## Purpose is to fill the 'alive' field.
stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive.
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
thisModule: int ## The module we're currently analysing for DCE.
alive: AliveSyms ## The final result of our computation.
options: TOptions
compilerProcs: Table[string, (int, int32)]
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
## "Exported to C" procs are special (these are marked with '.exportc') because these
## must not be optimized away!
let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId]
let flags = symPtr.flags
# due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly; issue (#411). However, the whole purpose here
# is to eliminate unused procs. So there is no special logic required for this case.
if sfCompileTime notin flags:
if ({sfExportc, sfCompilerProc} * flags != {}) or
(symPtr.kind == skMethod):
result = true
# XXX: This used to be a condition to:
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
if sfCompilerProc in flags:
c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId)
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
## be analysed.
if not c.alive[module].containsOrIncl(item):
var body = g[module].fromDisk.syms[item].ast
if body != emptyNodeId:
let opt = g[module].fromDisk.syms[item].options
if g[module].fromDisk.syms[item].kind in routineKinds:
body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos)
c.stack.add((module, opt, NodePos(body)))
when false:
let nid = g[module].fromDisk.syms[item].name
if nid != LitId(0):
let name = g[module].fromDisk.strings[nid]
if name in ["nimFrame", "callDepthLimitReached"]:
echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item
proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) =
let (module, item) = c.compilerProcs[name]
followLater(c, g, module, item)
proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind =
template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.types[t.item].kind
var t2 = translateId(t, g, c.thisModule, c.decoder.config)
result = t2.kind
while result in toSkip:
t2 = translateId(g[t2.module].fromDisk.types[t2.item].types[^1], g, t2.module, c.decoder.config)
result = t2.kind
proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Replicates the logic of `ccgexprs.genRangeChck`.
## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear
## the approach has enough merit.
var dest = loadTypeKind(n.typ, c, g, abstractVar)
if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}:
discard "no need to generate a check because it was disabled"
else:
let n0t = loadTypeKind(n.firstSon.typ, c, g, {})
if n0t in {tyUInt, tyUInt64}:
c.requestCompilerProc(g, "raiseRangeErrorNoArgs")
else:
let raiser =
case loadTypeKind(n.typ, c, g, abstractVarRange)
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
of tyFloat..tyFloat128: "raiseRangeErrorF"
else: "raiseRangeErrorI"
c.requestCompilerProc(g, raiser)
proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
## it is purely in a declarative context (type section etc.).
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
discard "ignore non-sym atoms"
of nkSym:
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, n.operand)
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let m = n1.litId
let item = n2.operand
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
followLater(c, g, otherModule, item)
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
nkCommentStmt, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkStaticStmt:
discard
of nkVarSection, nkLetSection, nkConstSection:
# XXX ignore the defining local variable name?
for son in sonsReadonly(tree, n):
aliveCode(c, g, tree, son)
of nkChckRangeF, nkChckRange64, nkChckRange:
rangeCheckAnalysis(c, g, tree, n)
of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
if n.firstSon.kind == nkSym and isNotGeneric(n):
let item = n.firstSon.operand
if isExportedToC(c, g, item):
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, item)
else:
for son in sonsReadonly(tree, n):
aliveCode(c, g, tree, son)
proc followNow(c: var AliveContext; g: PackedModuleGraph) =
## Mark all entries in the stack. Marking can add more entries
## to the stack but eventually we have looked at every alive symbol.
while c.stack.len > 0:
let (modId, opt, ast) = c.stack.pop()
c.thisModule = modId
c.options = opt
aliveCode(c, g, g[modId].fromDisk.bodies, ast)
proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
## Entry point for our DCE algorithm.
var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
thisModule: -1, alive: newSeq[IntSet](g.len),
options: conf.options)
for i in countdown(high(g), 0):
if g[i].status != undefined:
c.thisModule = i
for p in allNodes(g[i].fromDisk.topLevel):
aliveCode(c, g, g[i].fromDisk.topLevel, p)
followNow(c, g)
result = move(c.alive)
proc isAlive*(a: AliveSyms; module: int, item: int32): bool =
## Backends use this to query if a symbol is `alive` which means
## we need to produce (C/C++/etc) code for it.
result = a[module].contains(item)

56
compiler/ic/design.rst Normal file
View File

@@ -0,0 +1,56 @@
====================================
Incremental Recompilations
====================================
We split the Nim compiler into a frontend and a backend.
The frontend produces a set of `.rod` files. Every `.nim` module
produces its own `.rod` file.
- The IR must be a faithful representation of the AST in memory.
- The backend can do its own caching but doesn't have to. In the
current implementation the backend also caches its results.
Advantage of the "set of files" vs the previous global database:
- By construction, we either read from the `.rod` file or from the
`.nim` file, there can be no inconsistency. There can also be no
partial updates.
- No dependency to external packages (SQLite). SQLite simply is too
slow and the old way of serialization was too slow too. We use a
format designed for Nim and expect to base further tools on this
file format.
References to external modules must be (moduleId, symId) pairs.
The symbol IDs are module specific. This way no global ID increment
mechanism needs to be implemented that we could get wrong. ModuleIds
are rod-file specific too.
Global state
------------
There is no global state.
Rod File Format
---------------
It's a simple binary file format. `rodfiles.nim` contains some details.
Backend
-------
Nim programmers have to come to enjoy whole-program dead code elimination,
by default. Since this is a "whole program" optimization, it does break
modularity. However, thanks to the packed AST representation we can perform
this global analysis without having to unpack anything. This is basically
a mark&sweep GC algorithm:
- Start with the top level statements. Every symbol that is referenced
from a top level statement is not "dead" and needs to be compiled by
the backend.
- Every symbol referenced from a referenced symbol also has to be
compiled.
Caching logic: Only if the set of alive symbols is different from the
last run, the module has to be regenerated.

File diff suppressed because it is too large Load Diff

1231
compiler/ic/ic.nim Normal file

File diff suppressed because it is too large Load Diff

153
compiler/ic/integrity.nim Normal file
View File

@@ -0,0 +1,153 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Integrity checking for a set of .rod files.
## The set must cover a complete Nim project.
import sets
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs]
import packed_ast, bitabs, ic
type
CheckedContext = object
g: ModuleGraph
thisModule: int32
checkedSyms: HashSet[ItemId]
checkedTypes: HashSet[ItemId]
proc checkType(c: var CheckedContext; typeId: PackedItemId)
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId)
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos)
proc checkTypeObj(c: var CheckedContext; typ: PackedType) =
for child in typ.types:
checkType(c, child)
if typ.n != emptyNodeId:
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n)
if typ.sym != nilItemId:
checkForeignSym(c, typ.sym)
if typ.owner != nilItemId:
checkForeignSym(c, typ.owner)
checkType(c, typ.typeInst)
proc checkType(c: var CheckedContext; typeId: PackedItemId) =
if typeId == nilItemId: return
let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config)
if not c.checkedTypes.containsOrIncl(itemId):
let oldThisModule = c.thisModule
c.thisModule = itemId.module
checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item]
c.thisModule = oldThisModule
proc checkSym(c: var CheckedContext; s: PackedSym) =
if s.name != LitId(0):
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name
checkType c, s.typ
if s.ast != emptyNodeId:
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast)
if s.owner != nilItemId:
checkForeignSym(c, s.owner)
proc checkLocalSym(c: var CheckedContext; item: int32) =
let itemId = ItemId(module: c.thisModule, item: item)
if not c.checkedSyms.containsOrIncl(itemId):
checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item]
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config)
if not c.checkedSyms.containsOrIncl(itemId):
let oldThisModule = c.thisModule
c.thisModule = itemId.module
checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item]
c.thisModule = oldThisModule
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
if tree[n.int].typeId != nilItemId:
checkType(c, tree[n.int].typeId)
case n.kind
of nkEmpty, nkNilLit, nkType, nkNilRodNode:
discard
of nkIdent:
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
of nkSym:
checkLocalSym(c, tree.nodes[n.int].operand)
of directIntLit:
discard
of externIntLit, nkFloatLit..nkFloat128Lit:
assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId
of nkStrLit..nkTripleStrLit:
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
else:
for n0 in sonsReadonly(tree, n):
checkNode(c, tree, n0)
proc checkTree(c: var CheckedContext; t: PackedTree) =
for p in allNodes(t): checkNode(c, t, p)
proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) =
for symId in symIds:
assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len
proc checkModule(c: var CheckedContext; m: PackedModule) =
# We check that:
# - Every symbol references existing types and symbols.
# - Every tree node references existing types and symbols.
for i in 0..high(m.syms):
checkLocalSym c, int32(i)
checkTree c, m.toReplay
checkTree c, m.topLevel
for e in m.exports:
assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
for e in m.compilerProcs:
assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
checkLocalSymIds c, m, m.converters
checkLocalSymIds c, m, m.methods
checkLocalSymIds c, m, m.trmacros
checkLocalSymIds c, m, m.pureEnums
#[
To do: Check all these fields:
reexports*: seq[(LitId, PackedItemId)]
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
typeInstCache*: seq[(PackedItemId, PackedItemId)]
procInstCache*: seq[PackedInstantiation]
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
]#
proc checkIntegrity*(g: ModuleGraph) =
var c = CheckedContext(g: g)
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading:
assert false, "cannot check integrity: Module still loading"
of stored, storing, outdated, loaded:
c.thisModule = int32 i
checkModule(c, g.packed[i].fromDisk)

170
compiler/ic/navigator.nim Normal file
View File

@@ -0,0 +1,170 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Supports the "nim check --ic:on --defusages:FILE,LINE,COL"
## IDE-like features. It uses the set of .rod files to accomplish
## its task. The set must cover a complete Nim project.
import sets
from os import nil
from std/private/miscdollars import toLocation
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs, msgs, options]
import packed_ast, bitabs, ic
type
NavContext = object
g: ModuleGraph
thisModule: int32
trackPos: PackedLineInfo
alreadyEmitted: HashSet[string]
outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool =
if current.file == trackPos.file and current.line == trackPos.line:
let col = trackPos.col
if col >= current.col and col < current.col+tokenLen:
return true
proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
result = s.name != LitId(0) and
isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
let name = c.g.packed[s.module].fromDisk.syms[s.item].name
result = name != LitId(0) and
isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
const
EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
proc search(c: var NavContext; tree: PackedTree): ItemId =
# We use the linear representation here directly:
for i in 0..high(tree.nodes):
case tree.nodes[i].kind
of nkSym:
let item = tree.nodes[i].operand
if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info):
return ItemId(module: c.thisModule, item: item)
of nkModuleRef:
if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file:
let (n1, n2) = sons2(tree, NodePos i)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
if searchForeignSym(c, itemId, tree.nodes[i].info):
return itemId
else: discard
return EmptyItemId
proc isDecl(tree: PackedTree; n: NodePos): bool =
# XXX This is not correct yet.
const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
nkIdentDefs, nkEnumTy, nkVarTuple}
result = n.int >= 0 and tree[n.int].kind in declarativeNodes
proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
var m = ""
var file = c.g.packed[c.thisModule].fromDisk.strings[info.file]
if c.outputSep == ' ':
file = os.extractFilename file
toLocation(m, file, info.line.int, info.col.int + ColOffset)
if not c.alreadyEmitted.containsOrIncl(m):
msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
for i in 0..high(tree.nodes):
case tree.nodes[i].kind
of nkSym:
let item = tree.nodes[i].operand
if sym.item == item and sym.module == c.thisModule:
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
of nkModuleRef:
let (n1, n2) = sons2(tree, NodePos i)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
if itemId.item == sym.item and sym.module == itemId.module:
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
else: discard
proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
for i in 0..high(g.packed):
for k in 1..high(g.packed[i].fromDisk.includes):
# we start from 1 because the first "include" file is
# the module's filename.
if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0:
return i
return -1
proc nav(g: ModuleGraph) =
# translate the track position to a packed position:
let unpacked = g.config.m.trackPos
var mid = unpacked.fileIndex.int
let fullPath = toFullPath(g.config, unpacked.fileIndex)
if g.packed[mid].status == undefined:
# check if 'mid' is an include file of some other module:
mid = searchForIncludeFile(g, fullPath)
if mid < 0:
localError(g.config, unpacked, "unknown file name: " & fullPath)
return
let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath)
if fileId == LitId(0):
internalError(g.config, unpacked, "cannot find a valid file ID")
return
var c = NavContext(
g: g,
thisModule: int32 mid,
trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId),
outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
)
var symId = search(c, g.packed[mid].fromDisk.topLevel)
if symId == EmptyItemId:
symId = search(c, g.packed[mid].fromDisk.bodies)
if symId == EmptyItemId:
localError(g.config, unpacked, "no symbol at this position")
return
for i in 0..high(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading:
assert false, "cannot check integrity: Module still loading"
of stored, storing, outdated, loaded:
c.thisModule = int32 i
list(c, g.packed[i].fromDisk.topLevel, symId)
list(c, g.packed[i].fromDisk.bodies, symId)
proc navDefinition*(g: ModuleGraph) = nav(g)
proc navUsages*(g: ModuleGraph) = nav(g)
proc navDefusages*(g: ModuleGraph) = nav(g)
proc writeRodFiles*(g: ModuleGraph) =
for i in 0..high(g.packed):
case g.packed[i].status
of undefined, loading, stored, loaded:
discard "nothing to do"
of storing, outdated:
closeRodFile(g, g.packed[i].module)

419
compiler/ic/packed_ast.nim Normal file
View File

@@ -0,0 +1,419 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Packed AST representation, mostly based on a seq of nodes.
## For IC support. Far future: Rewrite the compiler passes to
## use this representation directly in all the transformations,
## it is superior.
import hashes, tables, strtabs
import bitabs
import ".." / [ast, options]
when defined(nimPreviewSlimSystem):
import std/assertions
type
SymId* = distinct int32
ModuleId* = distinct int32
NodePos* = distinct int
NodeId* = distinct int32
PackedItemId* = object
module*: LitId # 0 if it's this module
item*: int32 # same as the in-memory representation
const
nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
const
emptyNodeId* = NodeId(-1)
type
PackedLineInfo* = object
line*: uint16
col*: int16
file*: LitId
PackedLib* = object
kind*: TLibKind
generated*: bool
isOverriden*: bool
name*: LitId
path*: NodeId
PackedSym* = object
kind*: TSymKind
name*: LitId
typ*: PackedItemId
flags*: TSymFlags
magic*: TMagic
info*: PackedLineInfo
ast*: NodeId
owner*: PackedItemId
guard*: PackedItemId
bitsize*: int
alignment*: int # for alignment
options*: TOptions
position*: int
offset*: int
externalName*: LitId # instead of TLoc
locFlags*: TLocFlags
annex*: PackedLib
when hasFFI:
cname*: LitId
constraint*: NodeId
PackedType* = object
kind*: TTypeKind
callConv*: TCallingConvention
#nodekind*: TNodeKind
flags*: TTypeFlags
types*: seq[PackedItemId]
n*: NodeId
#nodeflags*: TNodeFlags
sym*: PackedItemId
owner*: PackedItemId
size*: BiggestInt
align*: int16
paddingAtEnd*: int16
# not serialized: loc*: TLoc because it is backend-specific
typeInst*: PackedItemId
nonUniqueId*: int32
PackedNode* = object # 28 bytes
kind*: TNodeKind
flags*: TNodeFlags
operand*: int32 # for kind in {nkSym, nkSymDef}: SymId
# for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
# for kind in nkInt32Lit: direct value
# for non-atom kinds: the number of nodes (for easy skipping)
typeId*: PackedItemId
info*: PackedLineInfo
PackedTree* = object ## usually represents a full Nim module
nodes*: seq[PackedNode]
PackedInstantiation* = object
key*, sym*: PackedItemId
concreteTypes*: seq[PackedItemId]
proc `==`*(a, b: SymId): bool {.borrow.}
proc hash*(a: SymId): Hash {.borrow.}
proc `==`*(a, b: NodePos): bool {.borrow.}
#proc `==`*(a, b: PackedItemId): bool {.borrow.}
proc `==`*(a, b: NodeId): bool {.borrow.}
proc newTreeFrom*(old: PackedTree): PackedTree =
result.nodes = @[]
when false: result.sh = old.sh
when false:
proc declareSym*(tree: var PackedTree; kind: TSymKind;
name: LitId; info: PackedLineInfo): SymId =
result = SymId(tree.sh.syms.len)
tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
proc litIdFromName*(tree: PackedTree; name: string): LitId =
result = tree.sh.strings.getOrIncl(name)
proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: kind, info: info,
operand: int32 getOrIncl(tree.sh.strings, token))
proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
proc throwAwayLastNode*(tree: var PackedTree) =
tree.nodes.setLen(tree.nodes.len-1)
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
# and this is why the IR is superior. We can copy subtrees
# via a linear scan.
let pos = n.int
let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
let d = dest.nodes.len
dest.nodes.setLen(d + L)
for i in 0..<L:
dest.nodes[d+i] = tree.nodes[pos+i]
when false:
proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
result = SymId(dest.sh.syms.len)
assert int(s) < tree.sh.syms.len
let oldSym = tree.sh.syms[s.int]
dest.sh.syms.add oldSym
type
PatchPos = distinct int
when false:
proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info,
typeId: typeId)
proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
result = PatchPos dest.nodes.len
dest.nodes.add source.nodes[sourcePos.int]
proc patch*(tree: var PackedTree; pos: PatchPos) =
let pos = pos.int
assert tree.nodes[pos].kind > nkNilLit
let distance = int32(tree.nodes.len - pos)
tree.nodes[pos].operand = distance
proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
tree.nodes[i]
proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
if tree.nodes[pos].kind > nkNilLit:
assert tree.nodes[pos].operand > 0
inc pos, tree.nodes[pos].operand
else:
inc pos
iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].operand
inc pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
let patchPos = prepare(dest, tree, n)
for x in sonsReadonly(tree, n): yield x
patch dest, patchPos
iterator isons*(dest: var PackedTree; tree: PackedTree;
n: NodePos): (int, NodePos) =
var i = 0
for ch0 in sons(dest, tree, n):
yield (i, ch0)
inc i
iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].operand
inc pos
if pos < last:
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
var count = 0
for child in sonsReadonly(tree, n):
inc count
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].operand
inc pos
while pos < last and count > 2:
yield NodePos pos
dec count
nextChild tree, pos
proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
# finding the parent of a node is rather easy:
var pos = n.int - 1
while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int)):
dec pos
#assert pos >= 0, "node has no parent"
result = NodePos(pos)
template parent*(n: NodePos): NodePos = parentImpl(tree, n)
proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool =
var count = 0
if tree.nodes[n.int].kind > nkNilLit:
for child in sonsReadonly(tree, n): inc count
result = count == x
proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
inc count
if count >= x: return true
return false
proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
NodePos(n.int+1)
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
tree.nodes[n.int].kind
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
LitId tree.nodes[n.int].operand
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
tree.nodes[n.int].info
template typ*(n: NodePos): PackedItemId =
tree.nodes[n.int].typeId
template flags*(n: NodePos): TNodeFlags =
tree.nodes[n.int].flags
template operand*(n: NodePos): int32 =
tree.nodes[n.int].operand
proc span*(tree: PackedTree; pos: int): int {.inline.} =
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (NodePos a, NodePos b)
proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (NodePos a, NodePos b, NodePos c)
proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
if count == i: return child
inc count
assert false, "node has no i-th child"
when false:
proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} =
tree.sh.strings[lit]
template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand
template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand
proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
when false:
# xxx `nkStrLit` or `nkStrLit..nkTripleStrLit:` below?
proc strLit*(tree: PackedTree; n: NodePos): lent string =
assert n.kind == nkStrLit
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
proc strVal*(tree: PackedTree; n: NodePos): string =
assert n.kind == nkStrLit
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
#result = cookedStrLit(raw)
proc filenameVal*(tree: PackedTree; n: NodePos): string =
case n.kind
of nkStrLit:
result = strVal(tree, n)
of nkIdent:
result = tree.sh.strings[n.litId]
of nkSym:
result = tree.sh.strings[tree.sh.syms[int n.symId].name]
else:
result = ""
proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
assert n.kind == nkIdent
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
const
externIntLit* = {nkCharLit,
nkIntLit,
nkInt8Lit,
nkInt16Lit,
nkInt64Lit,
nkUIntLit,
nkUInt8Lit,
nkUInt16Lit,
nkUInt32Lit,
nkUInt64Lit} # nkInt32Lit is missing by design!
externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
directIntLit* = nkInt32Lit
when false:
proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
if n.kind == nkIdent:
result = n.litId
elif n.kind == nkSym:
result = tree.sh.syms[int n.symId].name
else:
result = LitId(0)
template identId*(n: NodePos): LitId = identIdImpl(tree, n)
template copyInto*(dest, n, body) =
let patchPos = prepare(dest, tree, n)
body
patch dest, patchPos
template copyIntoKind*(dest, kind, info, body) =
let patchPos = prepare(dest, kind, info)
body
patch dest, patchPos
when false:
proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
let litId = tree.sh.strings.getKeyId(pragma)
if litId == LitId(0):
return false
assert n.kind == nkPragma
for ch0 in sonsReadonly(tree, n):
if ch0.kind == nkExprColonExpr:
if ch0.firstSon.identId == litId:
return true
elif ch0.identId == litId:
return true
proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
when false:
proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
let patchPos = prepare(dest, nkError, n.info)
dest.add nkStrLit, msg, n.info
copyTree(dest, tree, n)
patch dest, patchPos
iterator allNodes*(tree: PackedTree): NodePos =
var p = 0
while p < tree.len:
yield NodePos(p)
let s = span(tree, p)
inc p, s
proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
PackedItemId(module: LitId(0), item: item)

View File

@@ -14,16 +14,15 @@
import ".." / [ast, modulegraphs, trees, extccomp, btrees,
msgs, lineinfos, pathutils, options, cgmeth]
import std/tables
import tables
when defined(nimPreviewSlimSystem):
import std/assertions
proc replayStateChanges*(module: PSym; g: ModuleGraph; list: PNode) =
## `list` is an `nkStmtList` of `nkReplayAction` nodes (macro-cache puts/incs/
## adds/incls and a few pragmas) recorded for `module`. Under the NIF backend a
## loaded module's `ast` is never reconstructed, so the caller passes the replay
## actions it parsed out of the module's NIF directly.
import packed_ast, ic, bitabs
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
let list = module.ast
assert list != nil
assert list.kind == nkStmtList
for n in list:
@@ -67,9 +66,8 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph; list: PNode) =
g.cacheTables[destKey] = initBTree[string, PNode]()
if not contains(g.cacheTables[destKey], key):
g.cacheTables[destKey].add(key, val)
# else: the same key was already replayed. Under IC the import closure is
# replayed (direct module + transitive deps), so the same registration can
# legitimately be reached twice; re-applying it is a no-op, not an error.
else:
internalError(g.config, n.info, "key already exists: " & key)
of "incl":
let destKey = n[1].strVal
let val = n[2]
@@ -91,36 +89,72 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph; list: PNode) =
else:
internalAssert g.config, false
proc replayBackendActions*(g: ModuleGraph; module: PSym; list: PNode) =
## Applies the backend-relevant replay actions (C compile/link directives)
## found in a NIF-loaded module's top-level statement list. The `nifc`
## backend loads modules without going through sem's `replayStateChanges`,
## so e.g. math's `{.passL: "-lm".}` was lost and the final link failed
## with undefined references. VM cache actions are deliberately NOT
## replayed here — codegen does not run macros.
if list == nil: return
for n in list:
if n.kind == nkReplayAction and n.len >= 2 and
n[0].kind == nkStrLit and n[1].kind == nkStrLit:
case n[0].strVal
of "compile":
if n.len == 4 and n[2].kind == nkStrLit:
let cname = AbsoluteFile n[1].strVal
var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
obj: AbsoluteFile n[2].strVal,
flags: {CfileFlag.External},
customArgs: n[3].strVal)
extccomp.addExternalFileToCompile(g.config, cf)
of "link":
extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
of "passl":
extccomp.addLinkOption(g.config, n[1].strVal)
of "passc":
extccomp.addCompileOption(g.config, n[1].strVal)
of "localpassc":
extccomp.addLocalCompileOption(g.config, n[1].strVal,
toFullPathConsiderDirty(g.config, module.info.fileIndex))
of "cppdefine":
options.cppDefine(g.config, n[1].strVal)
else:
discard
proc replayBackendProcs*(g: ModuleGraph; module: int) =
for it in mitems(g.packed[module].fromDisk.attachedOps):
let key = translateId(it[0], g.packed, module, g.config)
let op = it[1]
let tmp = translateId(it[2], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[2])
g.attachedOps[op][key] = LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
let key = translateId(it[0], g.packed, module, g.config)
let tmp = translateId(it[1], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[1])
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
## We remember the generic instantiations a module performed
## in order to to avoid the code bloat that generic code tends
## to imply. This is cheaper than deduplication of identical
## generic instantiations. However, deduplication is more
## powerful and general and I hope to implement it soon too
## (famous last words).
assert g.packed[module].status == loaded
for it in g.packed[module].fromDisk.typeInstCache:
let key = translateId(it[0], g.packed, module, g.config)
g.typeInstCache.mgetOrPut(key, @[]).add LazyType(id: FullId(module: module, packed: it[1]), typ: nil)
for it in mitems(g.packed[module].fromDisk.procInstCache):
let key = translateId(it.key, g.packed, module, g.config)
let sym = translateId(it.sym, g.packed, module, g.config)
var concreteTypes = newSeq[FullId](it.concreteTypes.len)
for i in 0..high(it.concreteTypes):
let tmp = translateId(it.concreteTypes[i], g.packed, module, g.config)
concreteTypes[i] = FullId(module: tmp.module, packed: it.concreteTypes[i])
g.procInstCache.mgetOrPut(key, @[]).add LazyInstantiation(
module: module, sym: FullId(module: sym.module, packed: it.sym),
concreteTypes: concreteTypes, inst: nil)
for it in mitems(g.packed[module].fromDisk.methodsPerType):
let key = translateId(it[0], g.packed, module, g.config)
let col = it[1]
let tmp = translateId(it[2], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[2])
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
replayBackendProcs(g, module)
for it in mitems(g.packed[module].fromDisk.methods):
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
PackedItemId(module: LitId(0), item: it))
methodDef(g, g.idgen, sym)
when false:
# not used anymore:
for it in mitems(g.packed[module].fromDisk.compilerProcs):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1]))
g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId
for it in mitems(g.packed[module].fromDisk.converters):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].converters.add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.trmacros):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].patterns.add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.pureEnums):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil)

249
compiler/ic/rodfiles.nim Normal file
View File

@@ -0,0 +1,249 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Low level binary format used by the compiler to store and load various AST
## and related data.
##
## NB: this is incredibly low level and if you're interested in how the
## compiler works and less a storage format, you're probably looking for
## the `ic` or `packed_ast` modules to understand the logical format.
from typetraits import supportsCopyMem
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
## Overview
## ========
## `RodFile` represents a Rod File (versioned binary format), and the
## associated data for common interactions such as IO and error tracking
## (`RodFileError`). The file format broken up into sections (`RodSection`)
## and preceeded by a header (see: `cookie`). The precise layout, section
## ordering and data following the section are determined by the user. See
## `ic.loadRodFile`.
##
## A basic but "wrong" example of the lifecycle:
## ---------------------------------------------
## 1. `create` or `open` - create a new one or open an existing
## 2. `storeHeader` - header info
## 3. `storePrim` or `storeSeq` - save your stuff
## 4. `close` - and we're done
##
## Now read the bits below to understand what's missing.
##
## ### Issues with the Example
## Missing Sections:
## This is a low level API, so headers and sections need to be stored and
## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
## `loadSection`, respectively.
##
## No Error Handling:
## The API is centered around IO and prone to error, each operation checks or
## sets the `RodFile.err` field. A user of this API needs to handle these
## appropriately.
##
## API Notes
## =========
##
## Valid inputs for Rod files
## --------------------------
## ASTs, hopes, dreams, and anything as long as it and any children it may have
## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are:
## * string
## * objects & tuples (fields are recursed)
## * sequences AKA `seq[T]`
##
## Note on error handling style
## ----------------------------
## A flag based approach is used where operations no-op in case of a
## preexisting error and set the flag if they encounter one.
##
## Misc
## ----
## * 'Prim' is short for 'primitive', as in a non-sequence type
type
RodSection* = enum
versionSection
configSection
stringsSection
checkSumsSection
depsSection
numbersSection
exportsSection
hiddenSection
reexportsSection
compilerProcsSection
trmacrosSection
convertersSection
methodsSection
pureEnumsSection
toReplaySection
topLevelSection
bodiesSection
symsSection
typesSection
typeInstCacheSection
procInstCacheSection
attachedOpsSection
methodsPerTypeSection
enumToStringProcsSection
typeInfoSection # required by the backend
backendFlagsSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
RodFileError* = enum
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
includeFileChanged
RodFile* = object
f*: File
currentSection*: RodSection # for error checking
err*: RodFileError # little experiment to see if this works
# better than exceptions.
const
RodVersion = 1
cookie = [byte(0), byte('R'), byte('O'), byte('D'),
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
proc setError(f: var RodFile; err: RodFileError) {.inline.} =
f.err = err
#raise newException(IOError, "IO error")
proc storePrim*(f: var RodFile; s: string) =
## Stores a string.
## The len is prefixed to allow for later retreival.
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
if s.len != 0:
if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
setError f, ioFailure
proc storePrim*[T](f: var RodFile; x: T) =
## Stores a non-sequence/string `T`.
## If `T` doesn't support `copyMem` and is an object or tuple then the fields
## are written -- the user from context will need to know which `T` to load.
if f.err != ok: return
when supportsCopyMem(T):
if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
setError f, ioFailure
elif T is tuple:
for y in fields(x):
storePrim(f, y)
elif T is object:
for y in fields(x):
when y is seq:
storeSeq(f, y)
else:
storePrim(f, y)
else:
{.error: "unsupported type for 'storePrim'".}
proc storeSeq*[T](f: var RodFile; s: seq[T]) =
## Stores a sequence of `T`s, with the len as a prefix for later retrieval.
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
for i in 0..<s.len:
storePrim(f, s[i])
proc loadPrim*(f: var RodFile; s: var string) =
## Read a string, the length was stored as a prefix
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = newString(lenPrefix)
if lenPrefix > 0:
if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
setError f, ioFailure
proc loadPrim*[T](f: var RodFile; x: var T) =
## Load a non-sequence/string `T`.
if f.err != ok: return
when supportsCopyMem(T):
if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
setError f, ioFailure
elif T is tuple:
for y in fields(x):
loadPrim(f, y)
elif T is object:
for y in fields(x):
when y is seq:
loadSeq(f, y)
else:
loadPrim(f, y)
else:
{.error: "unsupported type for 'loadPrim'".}
proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
## `T` must be compatible with `copyMem`, see `loadPrim`
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = newSeq[T](lenPrefix)
for i in 0..<lenPrefix:
loadPrim(f, s[i])
proc storeHeader*(f: var RodFile) =
## stores the header which is described by `cookie`.
if f.err != ok: return
if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
setError f, ioFailure
proc loadHeader*(f: var RodFile) =
## Loads the header which is described by `cookie`.
if f.err != ok: return
var thisCookie: array[cookie.len, byte]
if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
setError f, ioFailure
elif thisCookie != cookie:
setError f, wrongHeader
proc storeSection*(f: var RodFile; s: RodSection) =
## update `currentSection` and writes the bytes value of s.
if f.err != ok: return
assert f.currentSection < s
f.currentSection = s
storePrim(f, s)
proc loadSection*(f: var RodFile; expected: RodSection) =
## read the bytes value of s, sets and error if the section is incorrect.
if f.err != ok: return
var s: RodSection
loadPrim(f, s)
if expected != s and f.err == ok:
setError f, wrongSection
proc create*(filename: string): RodFile =
## create the file and open it for writing
if not open(result.f, filename, fmWrite):
setError result, cannotOpen
proc close*(f: var RodFile) = close(f.f)
proc open*(filename: string): RodFile =
## open the file for reading
if not open(result.f, filename, fmRead):
setError result, cannotOpen

View File

@@ -1,283 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2026 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Precompiled config for the incremental compiler (`nim ic`).
##
## `nim ic` builds the program by spawning one `nim m` child per module (or
## strongly-connected import group) plus a final `nim nifc`. Each child is a
## full Nim process, so each would normally re-read the whole `nim.cfg` chain
## *and* re-run `config.nims` through the VM — work that is identical for every
## child and, because of the VM run, far from free. With ~85 modules in the
## compiler itself that config work is paid ~85 times during `koch bootic`.
##
## The fix mirrors Nimony's `.cfg.nif`: the driver parses config once, records
## the net effect, and the children replay it. Every config-file switch funnels
## through `processSwitch(..., passPP, ...)` (`nimconf.parseAssignment` and the
## `switch()` callback in `scriptconfig`), so the recorded sequence of those
## switches, replayed in order, reproduces an identical `ConfigRef` without any
## file read or VM run. The one config side effect that does not go through
## `processSwitch` is `cppDefine` (it mutates `conf.cppDefines` directly), so the
## resolved set is serialised alongside.
##
## Path-search switches are deliberately excluded from the recording (see
## `commands.processSwitch`): their resolved result already lives in
## `conf.searchPaths`, which the driver forwards to every child as absolute
## `--path` arguments; replaying their raw, config-dir-relative arguments here
## would misresolve.
import options, commands, lineinfos, pathutils, msgs
import std/[algorithm, os, sets, osproc, times, streams, syncio]
import "../dist/nimony/src/lib" / [nifbuilder, nifcoreparse]
const
IcConfigVersion* = "2"
## Artifact format version. Bump on any layout change here so a child built
## by an older compiler rejects a stale artifact and falls back to normal
## config loading instead of replaying a format it cannot parse.
proc writeIcConfig*(conf: ConfigRef; outfile: string) =
## Serialise the resolved config (the config-file switches recorded during
## `loadConfigs`, the resolved `cppDefines`/`searchPaths`, the nimcache dir, and
## the list of config *source* files for staleness detection) into `outfile`.
## `OnlyIfChanged`: when the content is byte-identical to what is already on
## disk the file is left untouched so its mtime does not advance — otherwise
## every `nim ic` run would re-fire the whole nifmake graph (see `nifler`'s
## `produceConfig`, whose model this mirrors).
var b = nifbuilder.open(outfile, writeMode = OnlyIfChanged)
b.withTree "stmts":
b.withTree "meta":
b.addStrLit IcConfigVersion
b.withTree "sources":
# Every config file read while loading (nim.cfg chain + config.nims), so a
# later run can decide via mtimes whether this artifact is still current
# (see `sourcesChanged`).
for f in conf.configFiles:
b.addStrLit f.string
b.withTree "nimcache":
# Resolved build nimcache. Recorded (unlike the path-search switches) so the
# driver, which replays this artifact instead of parsing `nim.cfg`, still
# learns a `--nimcache:` set inside `nim.cfg` and builds in the right place.
b.addStrLit conf.nimcacheDir.string
b.withTree "cppdefines":
# HashSet iteration order is unspecified; sort so the artifact is
# byte-stable across runs (nifmake keys rebuilds off content changes).
var defs: seq[string] = @[]
for d in conf.cppDefines: defs.add d
sort defs
for d in defs: b.addStrLit d
b.withTree "searchpaths":
# The resolved (absolute) search paths. Path-search *switches* are skipped
# below because their raw arguments are config-dir-relative; the net effect
# lives here instead, so a replayer with no `--path` command-line arguments
# (the `nim ic` driver itself) still resolves imports. `nim m`/`nim nifc`
# children also receive these as forwarded `--path` args; the dedup on
# replay makes the overlap harmless.
for p in conf.searchPaths:
b.addStrLit p.string
b.withTree "switches":
for sw in conf.icConfigSwitches:
b.addTree "sw"
b.addStrLit sw.switch
b.addStrLit sw.arg
b.endTree()
b.close()
proc applyIcConfig*(conf: ConfigRef; infile: string): bool =
## Replay the precompiled config into `conf`. Returns false (and applies
## nothing meaningful) when the artifact is missing or written by a compiler
## with an incompatible format version, so the caller can fall back to reading
## the config files normally.
if not fileExists(infile): return false
var pool = newPool()
var tags = newTagPool()
let
stmtsTag = tags.registerTag("stmts")
metaTag = tags.registerTag("meta")
sourcesTag = tags.registerTag("sources")
nimcacheTag = tags.registerTag("nimcache")
cppTag = tags.registerTag("cppdefines")
pathsTag = tags.registerTag("searchpaths")
switchesTag = tags.registerTag("switches")
swTag = tags.registerTag("sw")
var buf = parseFromFile(infile, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != stmtsTag:
endRead(c)
return false
var version = ""
var sawMeta = false
let info = unknownLineInfo
c.loopInto:
if c.kind == TagLit:
if c.cursorTagId == metaTag:
sawMeta = true
c.loopInto:
if c.kind == StrLit:
version = strVal(c)
inc c
else:
skip c
elif c.cursorTagId == nimcacheTag:
c.loopInto:
if c.kind == StrLit:
let nc = strVal(c)
# Only when nimcache was not already pinned on the command line: a
# `--nimcache:` argument the driver/child was launched with must win
# over whatever `nim.cfg` recorded into the artifact.
if nc.len > 0 and conf.nimcacheDir.isEmpty:
conf.nimcacheDir = AbsoluteDir(nc)
inc c
else:
skip c
elif c.cursorTagId == sourcesTag:
# Replay does not need the source list; it exists only for
# `sourcesChanged`. Skip the whole section.
skip c
elif c.cursorTagId == cppTag:
c.loopInto:
if c.kind == StrLit:
cppDefine(conf, strVal(c))
inc c
else:
skip c
elif c.cursorTagId == pathsTag:
c.loopInto:
if c.kind == StrLit:
# Append preserving the serialised order (which already reflects the
# driver's addPath insert-at-front sequence), deduping against any
# path a child already received via a forwarded `--path` argument.
let d = AbsoluteDir(strVal(c))
if not conf.searchPaths.contains(d): conf.searchPaths.add d
inc c
else:
skip c
elif c.cursorTagId == switchesTag:
c.loopInto:
if c.kind == TagLit and c.cursorTagId == swTag:
var sw = ""
var arg = ""
var idx = 0
c.loopInto:
if c.kind == StrLit:
if idx == 0: sw = strVal(c)
else: arg = strVal(c)
inc idx
inc c
else:
skip c
processSwitch(sw, arg, passPP, info, conf)
else:
skip c
else:
skip c
else:
skip c
endRead(c)
result = sawMeta and version == IcConfigVersion
proc sourcesChanged*(configFile: string): bool =
## True when the precompiled config at `configFile` is missing, malformed,
## written by an incompatible version, or any recorded config *source* file is
## newer than it (or has vanished) — i.e. the artifact must be regenerated.
## Mirrors nifler's `sourcesChanged`: the source list lives inside the artifact
## so this needs no out-of-band knowledge of which `nim.cfg`s were read.
if not fileExists(configFile): return true
let modtime = getLastModificationTime(configFile)
var pool = newPool()
var tags = newTagPool()
let
stmtsTag = tags.registerTag("stmts")
metaTag = tags.registerTag("meta")
sourcesTag = tags.registerTag("sources")
var buf = parseFromFile(configFile, 1000, pool, tags)
var c = beginRead(buf)
if c.kind != TagLit or c.cursorTagId != stmtsTag:
endRead(c)
return true
var version = ""
var depsChanged = false
c.loopInto:
if c.kind == TagLit and c.cursorTagId == metaTag:
c.loopInto:
if c.kind == StrLit:
version = strVal(c)
inc c
else:
skip c
elif c.kind == TagLit and c.cursorTagId == sourcesTag:
c.loopInto:
if c.kind == StrLit:
let dep = strVal(c)
if not fileExists(dep) or getLastModificationTime(dep) >= modtime:
depsChanged = true
inc c
else:
skip c
else:
skip c
endRead(c)
result = depsChanged or version != IcConfigVersion
proc produceIcConfig*(conf: ConfigRef) =
## The `cmdIcConfig` command. By the time it runs, the normal pipeline has
## already fully parsed the `nim.cfg` chain and run `config.nims`, so the
## resolved config is sitting in `conf`; just serialise it to `--o`.
let outPath = conf.icConfigOut
if outPath.len == 0:
rawMessage(conf, errGenerated, "icconfig: missing output path (--icConfigOut)")
return
createDir(parentDir(outPath))
writeIcConfig(conf, outPath)
proc ensureIcConfig*(conf: ConfigRef) =
## Driver-side (`cmdIc`). Make sure an up-to-date precompiled config exists,
## (re)producing it in a *separate* process when missing or stale, then point
## `conf.icPreparsedConfig` at it so the driver replays the very same config its
## `nim m`/`nim nifc` children will — perfect speed (config parsed at most once,
## skipped entirely when nothing changed) and consistency (one producer, every
## process replays its output). The artifact lives in the nimcache derived from
## the command line (pre-config-parse), which is the one the children are told;
## a `--nimcache:` set inside `nim.cfg` is recovered from the artifact itself.
let cacheDir = getNimcacheDir(conf).string
# Start from a clean cache when the on-disk NIF format stamp is absent or stale
# (see `icFormatVersion`). This must happen HERE, before the config artifact is
# produced — `commandIc` performs the same check later, but by then the artifact
# would already live in the cache and the wipe would delete it.
createDir(cacheDir)
let versionFile = cacheDir / "ic.version"
let stamp = if fileExists(versionFile): readFile(versionFile) else: ""
if stamp != icFormatVersion:
removeDir(cacheDir)
createDir(cacheDir)
writeFile(versionFile, icFormatVersion)
let outPath = cacheDir / "ic_config.cfg.nif"
if not fileExists(outPath) or sourcesChanged(outPath):
createDir(cacheDir)
# Re-invoke ourselves as the config producer: reuse this process's command
# line, dropping the command argument (`ic`) in favour of `icconfig` and the
# explicit output path, both BEFORE the project file (anything after the
# project is swallowed into `config.arguments` by `cmdLineRest`). The
# producer re-reads `nim.cfg` itself.
var pargs = @["icconfig", "--icConfigOut:" & outPath]
var droppedCmd = false
for a in commandLineParams():
if not droppedCmd and a.len > 0 and a[0] != '-':
droppedCmd = true # drop the original command token (`ic`)
else:
pargs.add a
let p = startProcess(getAppFilename(), args = pargs,
options = {poStdErrToStdOut})
let outp = p.outputStream.readAll()
let code = p.waitForExit()
p.close()
if code != 0 or not fileExists(outPath):
rawMessage(conf, errGenerated,
"failed to produce precompiled config (exit code " & $code & "):\n" & outp)
return
conf.icPreparsedConfig = outPath

View File

@@ -1,55 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2026 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nim's OWN module-suffix, replacing nimony's `gear2/modnames.moduleSuffix`.
##
## nimony's version hashes a path made RELATIVE to `getCurrentDir()` (or the
## shortest search-path-relative form), so the produced suffix depends on the
## current working directory AND the searchPath set. Under `nim ic` the
## DISCOVERY pass (`deps.nim`, in the driver process) and the COMPILE pass
## (`nifgen`/`typekeys`, in a child `nim m` process) can run with different CWDs
## or `--path` sets, so the SAME file hashes to two different suffixes: e.g.
## `std/staticos` became `sta5rk8sn1` at discovery but `sta4c0qxk` at compile, so
## every importer waited forever for a `.s.bif` that was actually written under
## the other name — a cold `nim ic` build (of anything pulling in `std/os`, whose
## `oscommon` does `from std/staticos import PathComponent`) never converged.
##
## Hashing the CANONICAL ABSOLUTE path makes the suffix a pure function of the
## file, identical across every process and call site. The base-name prefix +
## base-36 `uhash` layout is kept byte-for-byte compatible with the old scheme so
## nothing but the hashed string changes.
import std/os
import "../dist/nimony/src/lib" / tinyhashes
const
PrefixLen = 3 # keep it short: the suffix ends up in every mangled C name
Base36 = "0123456789abcdefghijklmnopqrstuvwxyz"
proc moduleSuffix*(path: string; searchPaths: openArray[string]): string =
## `searchPaths` is accepted for signature-compatibility with the replaced
## `modnames.moduleSuffix` but is deliberately IGNORED — the suffix must not
## depend on the search-path set or the CWD (see the module doc).
# Absolute inputs (the norm at every call site: `toFullPath`/`projectFull`)
# pass straight through `normalizedPath` with no `getCurrentDir` involvement;
# a stray relative path is made absolute against the CWD only as a fallback.
var f = path
if not isAbsolute(f):
try: f = absolutePath(f)
except CatchableError: discard
f = normalizedPath(f)
let m = splitFile(f).name
var id = uhash(f)
result = newStringOfCap(10)
for i in 0 ..< min(m.len, PrefixLen):
result.add m[i]
# base-36 of the hash, low digit first (order is irrelevant for identity).
while id > 0'u32:
result.add Base36[int(id mod 36'u32)]
id = id div 36'u32

View File

@@ -1,252 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2026 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## nifcore-based IC serialization helpers — Stage 1 of porting the IC backend
## from the old `nifstreams`/`nifcursors` NIF stack to `nifcore` (see
## `doc/ic_nifcore_port.md`).
##
## It hosts:
## * the process-wide shared `Pool`/`TagPool` that stands in for the old global
## `nifstreams.pool`,
## * `writeFileStable`, the content-stable file writer mirroring
## `nifcursors.writeFile(..., OnlyIfChanged)`,
## * the first ported writer (`writeSemDeps`), used as the migration spike.
##
## No `nifstreams`/`nifcursors` types cross this module's boundary: callers pass
## plain Nim values (config, ids, string lists), so it can coexist with the
## still-old-API `ast2nif.nim` during the migration.
import std / [syncio, algorithm]
from std / os import removeFile, moveFile
import options, pathutils, typekeys
import "../dist/nimony/src/lib" / [nifcore, nifcoreparse, nifreader, bif]
# One shared literals pool + tag pool for the whole process — the nifcore
# analogue of the old global `nifstreams.pool`. A single shared pool keeps
# string/symbol/file ids stable across every TokenBuf the IC backend builds,
# preserving the old global-pool semantics during the migration. (Stage 6 may
# move to fresh per-file pools for bif's fast path; see doc/ic_nifcore_port.md.)
let icPool* = newPool()
let icTags* = newTagPool()
proc createIcBuf*(cap = 16): TokenBuf {.inline.} =
## A `TokenBuf` bound to the shared IC pools.
createTokenBuf(cap, icPool, icTags)
proc tagId*(s: string): TagId {.inline.} =
## Intern a tag name in the shared tag pool.
icTags.registerTag(s)
type
IcBuilder* = object
## A thin nifcore `TokenBuf` builder whose surface is *primitive types only*
## (strings/ints/floats). It lets the still-old-API `ast2nif.nim` drive a
## nifcore buffer without any nifcore type crossing the module boundary —
## the bridge that routes IC output onto the nifcore serializer (Stage 2).
buf*: TokenBuf
proc newIcBuilder*(cap = 16): IcBuilder = IcBuilder(buf: createIcBuf(cap))
proc openTag*(b: var IcBuilder; tag: string) {.inline.} = b.buf.openTag(tagId(tag))
proc closeTag*(b: var IcBuilder) {.inline.} = b.buf.closeTag()
proc addSymUse*(b: var IcBuilder; s: string) {.inline.} = b.buf.addSymUse(s)
proc addSymDef*(b: var IcBuilder; s: string) {.inline.} = b.buf.addSymDef(s)
proc addIdent*(b: var IcBuilder; s: string) {.inline.} = b.buf.addIdent(s)
proc addStrLit*(b: var IcBuilder; s: string) {.inline.} = b.buf.addStrLit(s)
proc addIntLit*(b: var IcBuilder; v: int64) {.inline.} = b.buf.addIntLit(v)
proc addUIntLit*(b: var IcBuilder; v: uint64) {.inline.} = b.buf.addUIntLit(v)
proc addFloatLit*(b: var IcBuilder; v: float64) {.inline.} = b.buf.addFloatLit(v)
proc addCharLit*(b: var IcBuilder; c: char) {.inline.} = b.buf.addCharLit(c)
proc addDotToken*(b: var IcBuilder) {.inline.} = b.buf.addDotToken()
proc lineInfo*(b: var IcBuilder; file: string; line, col: int32; comment = "") =
## Attach line info (+ optional `#comment#`) to the head just emitted. No-op
## when `file` is empty (matches the old "emit only when info is valid").
## Strings are interned in the shared pools; the file/comment ids reproduce
## the old `pool.files`/`pool.strings` entries by string value.
if file.len == 0: return
let fid = icPool.filenames.getOrIncl(file)
let cid = if comment.len > 0: icPool.strings.getOrIncl(comment) else: StrId(0)
b.buf.appendLineInfo(fid, line, col, cid)
proc writeFileStable*(b: var TokenBuf; path: string; onlyIfChanged = false) =
## Serialize `b` to canonical module NIF text and write it. Mirrors
## `nifcursors.writeFile`: the module suffix is derived from `path`
## (`"." & extractModuleSuffix`), and `onlyIfChanged` skips the write when the
## on-disk bytes already match — the content-stability nifmake's incremental
## rebuild depends on.
let content = toModuleString(b, "." & extractModuleSuffix(path))
if onlyIfChanged:
let existing =
try: readFile(path)
except CatchableError: ""
if existing == content: return
writeFile(path, content)
proc writeStable*(b: var IcBuilder; path: string; onlyIfChanged = false) {.inline.} =
writeFileStable(b.buf, path, onlyIfChanged)
proc cursorPool*(c: Cursor): Pool {.inline.} = nifcore.pool(c)
## The literals pool the cursor's buffer was built against. `ast2nif.nim`
## imports `nifcore` with `except pool` (to keep nifstreams' global `pool`
## var the writer uses), so the reader reaches a cursor's pool through here —
## needed once `bif`-loaded buffers carry their OWN fresh pool rather than the
## shared `icPool`.
proc freshModuleCopy(b: var IcBuilder): TokenBuf =
## Re-home `b.buf` into a PRIVATE, module-local pool via `addSubtree` (which
## re-interns only the literals/tags this buffer actually uses). `b.buf` is bound
## to the process-wide shared `icPool`/`icTags`; storing it directly would embed
## the WHOLE shared pool (correct but huge — see `bif.storeToFile`). The copy's
## fresh-pool reload reproduces ids verbatim (the bif fresh-pool INVARIANT).
result = createTokenBuf(b.buf.len, newPool(), newTagPool())
var c = b.buf.beginRead()
while c.hasMore:
addSubtree(result, c)
skip c
proc storeBif*(b: var IcBuilder; path: string; dottedSuffix: string) =
## Persist the buffer as a compact, self-contained binary NIF (`.bif`).
var fresh = freshModuleCopy(b)
bif.store(fresh, path, dottedSuffix)
proc storeBifStable*(b: var IcBuilder; path: string; dottedSuffix: string) =
## Content-stable `bif` write — the binary analogue of `writeFileStable`'s
## `onlyIfChanged`: only replace `path` when the encoded bytes differ, so an
## unchanged sidecar keeps its mtime and nifmake prunes the dependent rebuild
## cascade. Used for the iface/impl cookies + dep sidecars whose byte-stability
## gates incremental builds. (bif encoding is deterministic for a given buffer
## under fresh pools, so equal content ⇒ equal bytes.)
var fresh = freshModuleCopy(b)
let tmp = path & ".tmp"
bif.store(fresh, tmp, dottedSuffix)
let newBytes = readFile(tmp)
let oldBytes =
try: readFile(path)
except CatchableError: ""
if newBytes == oldBytes:
removeFile(tmp)
else:
moveFile(tmp, path)
# --- subtree splicing (shared pool, so a raw subtree copy is exact) ----------
proc addAll*(dest: var IcBuilder; src: var IcBuilder) =
## Append every top-level subtree of `src` into `dest` — the nifcore analogue
## of the old `dest.add wholeBuffer` splice.
var c = src.buf.beginRead()
while c.hasMore:
addSubtree(dest.buf, c)
skip c
proc addStmtsBody*(dest: var IcBuilder; src: var IcBuilder) =
## Append the BODY of a `(stmts . . <body> )` builder into `dest`, dropping the
## wrapper tag and its two leading dot slots (flags/type) — the nifcore
## analogue of the old `for i in 3 ..< content.len-1: dest.add content[i]`.
var c = src.buf.beginRead() # at (stmts
c.into:
skip c # flags dot
skip c # type dot
while c.hasMore:
addSubtree(dest.buf, c)
skip c
# --- cookie input: a line-info-free logical token list of the module ---------
# The cookie hashers (ast2nif) need a flat, ParRi-bearing, index-addressable
# view of the serialized module. nifcore has no ParRi kind and variable-width
# tokens, so we flatten the buffer here (in the clean nifcore world) into a
# neutral `CookieTok` list — no nifcore type crosses into ast2nif.
type
CookieKind* = enum
ckParLe, ckParRi, ckSym, ckSymDef, ckIdent, ckStr, ckInt, ckUInt, ckFloat, ckChar, ckDot
CookieTok* = object
kind*: CookieKind
tag*: string # ckParLe
name*: string # ckSym / ckSymDef
sym*: uint32 # ckSym / ckSymDef id (identity key)
str*: string # ckIdent / ckStr
ival*: int64
uval*: uint64
fval*: float64
cval*: uint32
proc flattenGo(c: var Cursor; b: TokenBuf; acc: var seq[CookieTok]) =
while c.hasMore:
case c.kind
of TagLit:
acc.add CookieTok(kind: ckParLe, tag: b.tags.tagName(c.cursorTagId))
c.into:
flattenGo(c, b, acc)
acc.add CookieTok(kind: ckParRi)
of Symbol:
acc.add CookieTok(kind: ckSym, name: symName(c, b.pool), sym: uint32(symId(c, b.pool)))
skip c
of SymbolDef:
acc.add CookieTok(kind: ckSymDef, name: symName(c, b.pool), sym: uint32(symId(c, b.pool)))
skip c
of Ident:
acc.add CookieTok(kind: ckIdent, str: strVal(c, b.pool)); skip c
of StrLit:
acc.add CookieTok(kind: ckStr, str: strVal(c, b.pool)); skip c
of IntLit:
acc.add CookieTok(kind: ckInt, ival: intVal(c)); skip c
of UIntLit:
acc.add CookieTok(kind: ckUInt, uval: uintVal(c)); skip c
of FloatLit:
acc.add CookieTok(kind: ckFloat, fval: floatVal(c)); skip c
of CharLit:
acc.add CookieTok(kind: ckChar, cval: uint32(ord(charLit(c)))); skip c
of DotToken:
acc.add CookieTok(kind: ckDot); skip c
else:
skip c # LineInfoLit / ExtendedSuffix ride on heads, never standalone
proc flattenForCookie*(b: var IcBuilder): seq[CookieTok] =
## Flatten the nifcore module buffer to the cookie hashers' flat token list.
result = newSeqOfCap[CookieTok](b.buf.len)
var cur = b.buf.beginRead()
flattenGo(cur, b.buf, result)
proc collectBifStrLits*(path: string): seq[string] =
## Read a small `(tag "s" "s" …)` bif sidecar (`semdeps`/`edges`) and return every
## string literal it holds, in order — the binary analogue of the old nifstreams
## scan that collected `StrLit`s. Keeps nifcore types out of `deps.nim`, which
## only needs the recorded string list.
##
## Uses `loadFromFile` (a full read into owned memory) rather than the mmap-backed
## `bif.load`, then CLOSES the handle. `bif.load` intentionally leaves the mapping
## resident for the process lifetime; for the `nim ic` driver that reads these
## sidecars while `nim m` children rewrite them, a lingering read mapping is a
## Windows sharing violation: the child's `open(path, fmWrite)` fails with
## `IOError: cannot open`. These sidecars are tiny, so the zero-copy mmap buys
## nothing here anyway.
result = @[]
var f = open(path, fmRead)
var m = bif.loadFromFile(f)
close(f)
var c = m.buf.beginRead()
while c.hasMore:
if c.kind == StrLit: result.add strVal(c)
inc c
proc writeSemDeps*(config: ConfigRef; thisModule: int32; importPaths: seq[string]) =
## Stage 1 spike: the nifcore port of `ast2nif.writeSemDeps`. Serializes the
## module's resolved direct imports as `(semdeps "path" ...)`. Byte-identical
## to the old writer (verified), so `nim ic` build graphs are unaffected.
let selfSuffix = modname(thisModule, config)
var paths = importPaths
sort paths
var dest = newIcBuilder(4 + 2*paths.len)
dest.openTag "semdeps"
for p in paths:
dest.addStrLit p
dest.closeTag()
let path = toGeneratedFile(config, AbsoluteFile(selfSuffix), ".s.deps.bif").string
storeBifStable(dest, path, "." & extractModuleSuffix(path))

View File

@@ -11,8 +11,8 @@
# An identifier is a shared immutable string that can be compared by its
# id. This module is essential for the compiler's performance.
import wordrecg
import std/hashes
import
hashes, wordrecg
when defined(nimPreviewSlimSystem):
import std/assertions

View File

@@ -10,12 +10,10 @@
## This module implements the symbol importing mechanism.
import
ast, msgs, options, idents, lookups,
semdata, modulepaths, sigmatch, lineinfos,
modulegraphs, wordrecg
from std/strutils import `%`, startsWith, replace
from std/sequtils import addUnique
import std/[sets, tables, intsets]
intsets, ast, astalgo, msgs, options, idents, lookups,
semdata, modulepaths, sigmatch, lineinfos, sets,
modulegraphs, wordrecg, tables
from strutils import `%`
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -108,14 +106,13 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
else:
importPureEnumField(c, e)
else:
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
if hasPattern(s): addPattern(c, LazySym(sym: s))
if s.owner != origin:
c.exportIndirections.incl((origin.id, s.id))
proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) =
template bail = globalError(c.config, n.info, "invalid pragma")
result = (nil, @[])
if n.kind == nkPragmaExpr:
if n.len == 2 and n[1].kind == nkPragma:
result[0] = n[0]
@@ -144,7 +141,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
# for an enumeration we have to add all identifiers
if multiImport:
# for a overloadable syms add all overloaded routines
var it: ModuleIter = default(ModuleIter)
var it: ModuleIter
var e = initModuleIter(it, c.graph, fromMod, s.name)
while e != nil:
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
@@ -190,19 +187,22 @@ proc addImport(c: PContext; im: sink ImportedModule) =
template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
for it in mitems c.graph.ifaces[fromMod.position].converters:
if filter:
if sfExported in it.flags:
loadPackedSym(c.graph, it)
if sfExported in it.sym.flags:
addConverter(c, it)
for it in mitems c.graph.ifaces[fromMod.position].patterns:
if filter:
if sfExported in it.flags:
loadPackedSym(c.graph, it)
if sfExported in it.sym.flags:
addPattern(c, it)
for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
if filter:
importPureEnumFields(c, it, it.typ)
loadPackedSym(c.graph, it)
importPureEnumFields(c, it.sym, it.sym.typ)
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
addUnnamedIt(c, fromMod, it.name.id notin exceptSet)
addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
proc importAllSymbols*(c: PContext, fromMod: PSym) =
c.addImport ImportedModule(m: fromMod, mode: importAll)
@@ -228,10 +228,10 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
for i in 0..n.safeLen-1:
importForwarded(c, n[i], exceptSet, fromMod, importSet)
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
result = realModule
template createModuleAliasImpl(ident): untyped =
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
if n.kind != nkImportAs: discard
elif n.len != 2 or n[1].kind != nkIdent:
localError(c.config, n.info, "module alias must be an identifier")
@@ -242,18 +242,12 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, track
# avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
result = createModuleAliasImpl(realModule.name)
if importHidden:
ensureMutable result
result.optionsImpl.incl optImportHidden
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
result.info = moduleIdent.info
if trackUnusedImport:
c.unusedImports.add((result, result.info))
result.options.incl optImportHidden
c.unusedImports.add((result, n.info))
c.importModuleMap[result.id] = realModule.id
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
result = (nil, false)
var ret = default(typeof(result))
var ret: typeof(result)
proc processPragma(n2: PNode): PNode =
let (result2, kws) = splitPragmas(c, n2)
result = result2
@@ -288,10 +282,10 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
toFullPath(c.config, c.graph.importStack[i+1])
c.recursiveDep = err
let trackUnusedImport = warnUnusedImportX in c.config.notes
var realModule: PSym
discard pushOptionEntry(c)
let realModule = c.graph.importModuleCallback(c.graph, c.module, f)
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
result = importModuleAs(c, n, realModule, transf.importHidden)
popOptionEntry(c)
#echo "set back to ", L
@@ -304,26 +298,11 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
var prefix = ""
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
let moduleNameNorm = getModuleName(c.config, n).replace("\\", "/")
if belongsToStdlib(c.graph, result) and not startsWith(moduleNameNorm, stdPrefix) and
not startsWith(moduleNameNorm, "system/") and not startsWith(moduleNameNorm, "packages/"):
message(c.config, n.info, warnStdPrefix, realModule.name.s)
proc suggestMod(n: PNode; s: PSym) =
if n.kind == nkImportAs:
suggestMod(n[0], realModule)
elif n.kind == nkInfix:
suggestMod(n[2], s)
else:
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
suggestMod(n, result)
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info)
else:
result = nil
proc afterImport(c: PContext, m: PSym) =
if isCachedModule(c.graph, m): return
# fixes bug #17510, for re-exported symbols
let realModuleId = c.importModuleMap[m.id]
for s in allSyms(c.graph, m):
@@ -335,7 +314,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
let m = myImportModule(c, it, importStmtResult)
if m != nil:
# ``addDecl`` needs to be done before ``importAllSymbols``!
addDecl(c, m) # add symbol to symbol table of module
addDecl(c, m, it.info) # add symbol to symbol table of module
importAllSymbols(c, m)
#importForwarded(c, m.ast, emptySet, m)
afterImport(c, m)
@@ -344,24 +323,22 @@ proc evalImport*(c: PContext, n: PNode): PNode =
result = newNodeI(nkImportStmt, n.info)
for i in 0..<n.len:
let it = n[i]
if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
let lastPos = it.len - 1
var imp = copyNode(it)
newSons(imp, it.len)
for i in 0 ..< lastPos: imp[i] = it[i]
imp[lastPos] = imp[0] # dummy entry, replaced in the loop
for x in it[lastPos]:
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
let sep = it[0]
let dir = it[1]
var imp = newNodeI(nkInfix, it.info)
imp.add sep
imp.add dir
imp.add sep # dummy entry, replaced in the loop
for x in it[2]:
# transform `a/b/[c as d]` to `/a/b/c as d`
if x.kind == nkInfix and x[0].ident.s == "as":
var impAs = copyNode(x)
newSons(impAs, 3)
impAs[0] = x[0]
imp[lastPos] = x[1]
let impAs = copyTree(x)
imp[2] = x[1]
impAs[1] = imp
impAs[2] = x[2]
impMod(c, impAs, result)
impMod(c, imp, result)
else:
imp[lastPos] = x
imp[2] = x
impMod(c, imp, result)
else:
impMod(c, it, result)
@@ -372,7 +349,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
var m = myImportModule(c, n[0], result)
if m != nil:
n[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
addDecl(c, m, n.info) # add symbol to symbol table of module
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
for i in 1..<n.len:
@@ -387,7 +364,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
var m = myImportModule(c, n[0], result)
if m != nil:
n[0] = newSymNode(m)
addDecl(c, m) # add symbol to symbol table of module
addDecl(c, m, n.info) # add symbol to symbol table of module
importAllSymbolsExcept(c, m, readExceptSet(c, n))
#importForwarded(c, m.ast, exceptSet, m)
afterImport(c, m)

View File

@@ -14,17 +14,15 @@
## See doc/destructors.rst for a spec of the implemented rewrite rules
import
ast, astalgo, msgs, renderer, magicsys, types, idents,
options, lowerings, modulegraphs,
intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
strutils, options, lowerings, tables, modulegraphs,
lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
varpartitions, aliasanalysis, dfa, wordrecg
import std/[strtabs, tables, strutils, intsets]
varpartitions, aliasanalysis, dfa
when defined(nimPreviewSlimSystem):
import std/assertions
from trees import exprStructuralEquivalent, getRoot, whichPragma, getPotentialWrites
from trees import exprStructuralEquivalent, getRoot
type
Con = object
@@ -37,8 +35,6 @@ type
idgen: IdGenerator
body: PNode
otherUsage: TLineInfo
inUncheckedAssignSection: int
inEnsureMove: int
Scope = object # we do scope-based memory management.
# a scope is comparable to an nkStmtListExpr like
@@ -69,25 +65,23 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
result = ast.hasDestructor(t)
when toDebug.len > 0:
# for more effective debugging
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
assert(not containsGarbageCollectedRef(t))
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo; needsInit: bool): PNode =
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info)
sym.typ = typ
if not needsInit:
sym.incl sfNoInit
s.vars.add(sym)
result = newSymNode(sym)
proc nestedScope(parent: var Scope; body: PNode): Scope =
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
type
MoveOrCopyFlag = enum
IsDecl, IsExplicitSink, IsReturn
IsDecl, IsExplicitSink
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
@@ -102,7 +96,6 @@ when false:
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
let root = parampatterns.exprRoot(n, allowCalls=false)
if root == nil: return false
elif sfSingleUsedTemp in root.flags: return true
var s = addr(scope)
while s != nil:
@@ -165,16 +158,12 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
else:
result = false
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc} and
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
if not hasDestructorOrAsgn(c, n.typ): return true
if not hasDestructor(c, n.typ): return true
let m = skipConvDfa(n)
result = isLastReadImpl(n, c, s)
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
isLastReadImpl(n, c, s)
proc isFirstWrite(n: PNode; c: var Con): bool =
let m = skipConvDfa(n)
@@ -191,21 +180,14 @@ proc isCursor(n: PNode): bool =
else:
false
template isFullyUnpackedTuple(n: PNode): bool =
template isUnpackedTuple(n: PNode): bool =
## we move out all elements of unpacked tuples,
## hence unpacked tuples themselves don't need to be destroyed
## except it's already a cursor
## restricted to `skTemp`, tuple temps where not every field is unpacked should not use `skTemp`
(n.kind == nkSym and n.sym.kind == skTemp and
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
(n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) =
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
if inferredFromCopy:
m.add ", which is inferred from unavailable '=copy'"
if (opname == "=" or opname == "=copy" or opname == "=dup") and
ri != nil:
if (opname == "=" or opname == "=copy") and ri != nil:
m.add "; requires a copy because it's not the last read of '"
m.add renderTree(ri)
m.add '\''
@@ -220,43 +202,25 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
m.add " a 'sink' parameter"
m.add "; routine: "
m.add c.owner.name.s
#m.add "\n\n"
#m.add renderTree(c.body, {renderIds})
localError(c.graph.config, ri.info, errGenerated, m)
proc makePtrType(c: var Con, baseType: PType): PType =
result = newType(tyPtr, c.idgen, c.owner)
result = newType(tyPtr, nextTypeId c.idgen, c.owner)
addSonSkipIntLit(result, baseType, c.idgen)
proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
var addrExp: PNode
if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
addrExp = dest
else:
addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
addrExp.add(dest)
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
addrExp.add(dest)
result = newTree(nkCall, newSymNode(op), addrExp)
proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
var op = getAttachedOp(c.graph, t, kind)
if op == nil or op.ast.isGenericRoutine:
# give up and find the canonical type instead:
let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct})
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil:
op = getAttachedOp(c.graph, canon, kind)
if op == nil or op.ast.isGenericRoutine:
# IC: injectDestructorCalls is demand-driven and runs HERE (cg), not in the
# `lower` stage, so a structural, env-agnostic op the lower stage never had
# reason to serialize — most often a closure PROC type's `=destroy`/`=sink`
# (which act on the `(ClP_0, ClE_0)` tuple, NOT the concrete env) — must be
# lifted on demand, exactly as the lazy path's cg does. This is safe now:
# closure-env identity resolves via `attachedOps[itemId]`/env-erased typeKey,
# env objects load complete, and atomicRefOp's type-erased path covers any
# still-incomplete env (so the lift never walks a nil field).
excl t.flagsImpl, tfCheckedForDestructor
createTypeBoundOps(c.graph, nil, t, dest.info, c.idgen)
op = getAttachedOp(c.graph, t, kind)
if op == nil:
#echo dest.typ.id
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
@@ -267,12 +231,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
dbg:
if kind == attachedDestructor:
echo "destructor is ", op.id, " ", op.ast
if sfError in op.flags:
if ri != nil:
checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
else:
# uses the lineinfos of `dest` is `ri` is not available
checkForErrorPragma(c, t, dest, AttachedOpToStr[kind])
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
c.genOp(op, dest)
proc genDestroy(c: var Con; dest: PNode): PNode =
@@ -289,20 +248,10 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
proc isNoInit(dest: PNode): bool {.inline.} =
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
proc deepAliases(dest, ri: PNode): bool =
case ri.kind
of nkCallKinds, nkStmtListExpr, nkBracket, nkTupleConstr, nkObjConstr,
nkCast, nkConv, nkObjUpConv, nkObjDownConv:
for r in ri:
if deepAliases(dest, r): return true
return false
else:
return aliases(dest, ri) != no
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
isNoInit(dest) or IsReturn in flags:
isNoInit(dest):
# optimize sink call into a bitwise memcopy
result = newTree(nkFastAsgn, dest, ri)
else:
@@ -313,19 +262,12 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
else:
# the default is to use combination of `=destroy(dest)` and
# and copyMem(dest, source). This is efficient.
if deepAliases(dest, ri):
# consider: x = x + y, it is wrong to destroy the destination first!
# tmp to support self assignments
let tmp = c.getTemp(s, dest.typ, dest.info, needsInit = false)
result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
c.genDestroy(tmp))
else:
result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
proc isCriticalLink(dest: PNode): bool {.inline.} =
#[
Lins's idea that only "critical" links can introduce a cycle. This is
critical for the performance guarantees that we strive for: If you
critical for the performance gurantees that we strive for: If you
traverse a data structure, no tracing will be performed at all.
ORC is about this promise: The GC only touches the memory that the
mutator touches too.
@@ -342,17 +284,16 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
]#
result = dest.kind != nkSym
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
if c.graph.config.selectedGC in {gcOrc, gcYrc} and IsExplicitSink notin flags:
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(c.graph, t):
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
if c.graph.config.selectedGC == gcOrc:
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(t):
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
proc genMarkCyclic(c: var Con; result, dest: PNode) =
if c.graph.config.selectedGC in {gcOrc, gcYrc}:
if c.graph.config.selectedGC == gcOrc:
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(c.graph, t):
if cyclicType(t):
if t.kind == tyRef:
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
else:
@@ -366,9 +307,6 @@ proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
assert ri.typ != nil
proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
if c.inEnsureMove > 0:
localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri &
"', which introduces an implicit copy")
let t = dest.typ
if tfHasOwned in t.flags and ri.kind != nkNilLit:
# try to improve the error message here:
@@ -385,7 +323,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
# but fields within active case branch might need destruction
# tmp to support self assignments
let tmp = c.getTemp(s, n[1].typ, n.info, needsInit = false)
let tmp = c.getTemp(s, n[1].typ, n.info)
result = newTree(nkStmtList)
result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
@@ -397,38 +335,30 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
if hasDestructor(c, objType):
if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
sfOverridden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
result.add newTree(nkFastAsgn, le, tmp)
return
# generate: if le != tmp: `=destroy`(le)
if c.inUncheckedAssignSection != 0:
let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
cond.add le
cond.add tmp
let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
notExpr.add cond
result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
cond.add le
cond.add tmp
let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
notExpr.add cond
result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
result.add newTree(nkFastAsgn, le, tmp)
proc genWasMoved(c: var Con, n: PNode): PNode =
let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, n.typ, attachedWasMoved)
if op != nil:
if sfError in op.flags:
c.checkForErrorPragma(n.typ, n, "=wasMoved")
result = genOp(c, op, n)
else:
result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
result.add copyTree(n) #mWasMoved does not take the address
#if n.kind != nkSym:
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
result.add copyTree(n) #mWasMoved does not take the address
#if n.kind != nkSym:
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
result = newNodeI(nkCall, info)
@@ -437,16 +367,13 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
# generate: (let tmp = v; reset(v); tmp)
if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0:
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or
(n.typ.kind == tyPtr and n.sym.typ.kind == tyRef)
# bug #23505; transformed by `transf`: addr (deref ref) -> ptr
# we know it's really a pointer; so here we assign it directly
if not hasDestructor(c, n.typ):
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
result = copyTree(n)
else:
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info)
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info)
temp.typ = n.typ
var v = newNodeI(nkLetSection, n.info)
let tempAsNode = newSymNode(temp)
@@ -459,8 +386,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
result.add v
let nn = skipConv(n)
if hasDestructor(c, n.typ):
c.genMarkCyclic(result, nn)
c.genMarkCyclic(result, nn)
let wasMovedCall = c.genWasMoved(nn)
result.add wasMovedCall
result.add tempAsNode
@@ -468,67 +394,40 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
proc isCapturedVar(n: PNode): bool =
let root = getRoot(n)
if root != nil: result = root.name.s[0] == ':'
else: result = false
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
if not hasDestructorOrAsgn(c, nTyp):
# Non-managed (plain-old-data) type: no ownership transfer is needed.
# Return the expression directly — no temp required.
if c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
assert(not containsManagedMemory(nTyp))
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
return p(n, c, s, normal)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = c.getTemp(s, nTyp, n.info, needsInit = false)
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
let tmp = c.getTemp(s, n.typ, n.info)
if hasDestructor(c, n.typ):
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, {}, isFromSink = true)
c.finishCopy(m, n, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
else:
if c.graph.config.selectedGC in {gcArc, gcOrc}:
assert(not containsManagedMemory(n.typ))
if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
# Since we know somebody will take over the produced copy, there is
# no need to destroy it.
result.add tmp
proc isDangerousSeq(t: PType): bool {.inline.} =
let t = t.skipTypes(abstractInst)
result = t.kind == tySequence and tfHasOwned notin t.elementType.flags
result = t.kind == tySequence and tfHasOwned notin t[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, nkCast:
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
result = containsConstSeq(n[1])
of nkObjConstr, nkClosure:
for i in 1..<n.len:
@@ -545,15 +444,15 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
# produce temp creation for (fn, env). But we need to move 'env'?
# This was already done in the sink parameter handling logic.
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
let tmp = c.getTemp(s, arg.typ, arg.info, true)
result.add c.genSink(s, tmp, arg, {IsDecl})
let tmp = c.getTemp(s, arg.typ, arg.info)
result.add c.genSink(tmp, arg, {IsDecl})
result.add tmp
s.final.add c.genDestroy(tmp)
else:
result = arg
proc cycleCheck(n: PNode; c: var Con) =
if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return
if c.graph.config.selectedGC != gcArc: return
var value = n[1]
if value.kind == nkClosure:
value = value[1]
@@ -585,7 +484,7 @@ proc cycleCheck(n: PNode; c: var Con) =
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
# move the variable declaration to the top of the frame:
s.vars.add v.sym
if isFullyUnpackedTuple(v):
if isUnpackedTuple(v):
if c.inLoop > 0:
# unpacked tuple needs reset at every loop iteration
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
@@ -624,11 +523,9 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
# later and use it to eliminate the temporary when theres no need for it, but its
# tricky because you would have to intercept moveOrCopy at a certain point
let tmp = c.getTemp(s.parent[], ret.typ, ret.info, needsInit = true)
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
tmp.sym.flags = tmpFlags
let cpy = if hasDestructor(c, ret.typ) and
ret.typ.kind notin {tyOpenArray, tyVarargs}:
# bug #23247 we don't own the data, so it's harmful to destroy it
let cpy = if hasDestructor(c, ret.typ):
s.parent[].final.add c.genDestroy(tmp)
moveOrCopy(tmp, ret, c, s, {IsDecl})
else:
@@ -680,7 +577,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
for j in 0 ..< it.len-1:
branch[j] = copyTree(it[j])
var ofScope = nestedScope(s, it.lastSon)
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
else:
processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
@@ -728,7 +625,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
#Condition needs to be destroyed outside of the condition/branch scope
branch[0] = p(it[0], c, s, normal)
branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
processScope(c, branchScope, maybeVoid(it[^1], branchScope))
else:
processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
@@ -756,27 +653,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
of nkWhen: # This should be a "when nimvm" node.
result = copyTree(n)
result[1][0] = processCall(n[1][0], s)
of nkPragmaBlock:
var inUncheckedAssignSection = 0
let pragmaList = n[0]
for pi in pragmaList:
if whichPragma(pi) == wCast:
case whichPragma(pi[1])
of wUncheckedAssign:
inUncheckedAssignSection = 1
else:
discard
result = shallowCopy(n)
inc c.inUncheckedAssignSection, inUncheckedAssignSection
for i in 0 ..< n.len-1:
result[i] = p(n[i], c, s, normal)
result[^1] = maybeVoid(n[^1], s)
dec c.inUncheckedAssignSection, inUncheckedAssignSection
else:
result = nil
assert(false)
else: assert(false)
proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
@@ -785,10 +662,10 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result = copyNode(n)
result.add call
else:
let tmp = c.getTemp(s, n[0].typ, n.info, needsInit = true)
let tmp = c.getTemp(s, n[0].typ, n.info)
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
m.add p(n[0], c, s, normal)
c.finishCopy(m, n[0], {}, isFromSink = false)
c.finishCopy(m, n[0], isFromSink = false)
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
var toDisarm = n[0]
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
@@ -803,38 +680,9 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result.add copyNode(n[0])
s.needsTry = true
template isCustomDestructor(c: Con, t: PType): bool =
hasDestructor(c, t) and
getAttachedOp(c.graph, t, attachedDestructor) != nil and
sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags
proc hasCustomDestructor(c: Con, t: PType): bool =
result = isCustomDestructor(c, t)
var obj = t
while obj.baseClass != nil:
obj = skipTypes(obj.baseClass, abstractPtrs)
result = result or isCustomDestructor(c, obj)
const
exprBranchKinds = {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt,
nkTryStmt, nkPragmaBlock}
proc distributeAsgn(asgnKind: TNodeKind; dest, ri: PNode; c: var Con; s: var Scope): PNode =
## Distributes an assignment ``dest = ri`` into the leaf expressions of
## ``ri`` when ``ri`` is an expression-based control flow construct. This
## avoids creating pointless intermediate temporaries (bug #25850). The
## descent is recursive so that nestings like ``block: ...; if c: a else: b``
## assign directly to ``dest`` instead of going through a temp per branch.
if ri.kind in exprBranchKinds:
template process(child, s): untyped =
distributeAsgn(asgnKind, dest, child, c, s)
handleNestedTempl(ri, process, willProduceStmt = true)
else:
result = newTree(asgnKind, dest, p(ri, c, s, consumed))
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode =
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt}:
template process(child, s): untyped = p(child, c, s, mode)
handleNestedTempl(n, process, tmpFlags = tmpFlags)
elif mode == sinkArg:
@@ -865,9 +713,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
elif n.kind in {nkObjDownConv, nkObjUpConv}:
result = copyTree(n)
result[0] = p(n[0], c, s, sinkArg)
elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
result = copyTree(n)
result[1] = p(n[1], c, s, sinkArg)
elif n.typ == nil:
# 'raise X' can be part of a 'case' expression. Deal with it here:
result = p(n, c, s, normal)
@@ -911,14 +756,13 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
for i in 1..<n.len:
if n[i].kind == nkExprColonExpr:
let field = lookupFieldAgain(t, n[i][0].sym)
if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}):
# don't sink fields with openarray types
if field != nil and sfCursor in field.flags:
result[i][1] = p(n[i][1], c, s, normal)
else:
result[i][1] = p(n[i][1], c, s, m)
else:
result[i] = p(n[i], c, s, m)
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
if mode == normal and isRefConstr:
result = ensureDestruction(result, n, c, s)
of nkCallKinds:
let inSpawn = c.inSpawn
@@ -927,9 +771,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
elif c.inSpawn > 0:
c.inSpawn.dec
# bug #23907; skips tyGenericInst for generic callbacks
let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
let L = if parameters != nil: parameters.signatureLen else: 0
let parameters = n[0].typ
let L = if parameters != nil: parameters.len else: 0
when false:
var isDangerous = false
@@ -938,19 +781,13 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
isDangerous = true
result = shallowCopy(n)
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
inc c.inEnsureMove
result[1] = p(n[1], c, s, sinkArg)
dec c.inEnsureMove
else:
for i in 1..<n.len:
if i < L and isCompileTimeOnly(parameters[i]):
result[i] = n[i]
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
result[i] = p(n[i], c, s, sinkArg)
else:
result[i] = p(n[i], c, s, normal)
for i in 1..<n.len:
if i < L and isCompileTimeOnly(parameters[i]):
result[i] = n[i]
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
result[i] = p(n[i], c, s, sinkArg)
else:
result[i] = p(n[i], c, s, normal)
when false:
if isDangerous:
@@ -958,17 +795,14 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
result[0] = copyTree(n[0])
if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc, gcYrc}:
if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
let destroyOld = c.genDestroy(result[1])
result = newTree(nkStmtList, destroyOld, result)
else:
result[0] = p(n[0], c, s, normal)
if canRaise(n[0]): s.needsTry = true
if mode == normal:
if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}:
# Returns of openarray types shouldn't be destroyed
# bug #19435; # bug #23247
result = ensureDestruction(result, n, c, s)
result = ensureDestruction(result, n, c, s)
of nkDiscardStmt: # Small optimization
result = shallowCopy(n)
if n[0].kind != nkEmpty:
@@ -978,9 +812,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
of nkVarSection, nkLetSection:
# transform; var x = y to var x; x op y where op is a move or copy
result = newNodeI(nkStmtList, n.info)
let isInProc = c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}
for it in n:
var ri = it[^1]
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
@@ -996,20 +827,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
s.locals.add v.sym
pVarTopLevel(v, c, s, result)
if ri.kind != nkEmpty:
let isGlobalPragma = v.kind == nkSym and
{sfPure, sfGlobal} <= v.sym.flags and
isInProc
if isGlobalPragma:
c.graph.procGlobals.add newTree(nkFastAsgn, v, ri)
else:
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
result.add value
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
elif ri.kind == nkEmpty and c.inLoop > 0:
let skipInit = v.kind == nkDotExpr and # Closure var
sfNoInit in v[1].sym.flags
if not skipInit:
result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {})
result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {})
else: # keep the var but transform 'ri':
var v = copyNode(n)
var itCopy = copyNode(it)
@@ -1027,17 +847,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
cycleCheck(n, c)
assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
if inReturn:
flags.incl(IsReturn)
let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
elif isDiscriminantField(n[0]):
result = c.genDiscriminantAsgn(s, n)
elif n[1].kind in exprBranchKinds:
# Distribute the assignment into each branch to avoid
# creating pointless temporaries for expression-based control flow.
let dest = p(n[0], c, s, mode)
result = distributeAsgn(n.kind, dest, n[1], c, s)
else:
result = copyNode(n)
result.add p(n[0], c, s, mode)
@@ -1054,7 +867,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
result = n
of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange:
of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock:
result = shallowCopy(n)
for i in 0 ..< n.len:
result[i] = p(n[i], c, s, normal)
@@ -1118,7 +931,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
of nkReturnStmt:
result = shallowCopy(n)
for i in 0..<n.len:
result[i] = p(n[i], c, s, mode, inReturn=true)
result[i] = p(n[i], c, s, mode)
s.needsTry = true
of nkCast:
result = shallowCopy(n)
@@ -1132,12 +945,11 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
of nkGotoState, nkState, nkAsmStmt:
result = n
else:
result = nil
internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind)
proc sameLocation*(a, b: PNode): bool =
proc sameConstant(a, b: PNode): bool =
a.kind in nkLiterals and b.kind in nkLiterals and a.intVal == b.intVal
a.kind in nkLiterals and a.intVal == b.intVal
const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
if a.kind in nkEndPoint and b.kind in nkEndPoint:
@@ -1161,9 +973,9 @@ proc sameLocation*(a, b: PNode): bool =
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
else: false
proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
# with side effects
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info)
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
temp.typ = ri[1].typ
var v = newNodeI(nkLetSection, ri[1].info)
let tempAsNode = newSymNode(temp)
@@ -1178,41 +990,14 @@ proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags:
newAccess.add ri[0]
newAccess.add tempAsNode
var snk = c.genSink(s, dest, newAccess, flags)
var snk = c.genSink(dest, newAccess, flags)
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode =
var n = orig
while true:
case n.kind
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
n = n[0]
else:
break
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
let tmp = c.getTemp(s, n.typ, n.info, needsInit = true)
tmp.sym.flagsImpl.incl sfSingleUsedTemp
result.add newTree(nkFastAsgn, tmp, copyTree(n))
s.final.add c.genDestroy(tmp)
n[] = tmp[]
result.add copyTree(orig)
else:
result = nil
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
var ri = ri
var isEnsureMove = 0
if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mEnsureMove:
ri = ri[1]
isEnsureMove = 1
if sameLocation(dest, ri):
# rule (self-assignment-removal):
result = newNodeI(nkEmpty, dest.info)
elif isCursor(dest) or dest.typ.kind in {tyOpenArray, tyVarargs}:
# hoisted openArray parameters might end up here
# openArray types don't have a lifted assignment operation (it's empty)
# bug #22132
elif isCursor(dest):
case ri.kind:
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
@@ -1221,86 +1006,69 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
else:
result = newTree(nkFastAsgn, dest, p(ri, c, s, normal))
else:
let ri2 = if ri.kind == nkWhen: ri[1][0] else: ri
case ri2.kind
case ri.kind
of nkCallKinds:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
result = c.genSink(dest, p(ri, c, s, consumed), flags)
of nkBracketExpr:
if isFullyUnpackedTuple(ri[0]):
if isUnpackedTuple(ri[0]):
# unpacking of tuple: take over the elements
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
result = c.genSink(dest, p(ri, c, s, consumed), flags)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
if aliases(dest, ri) == no:
# Rule 3: `=sink`(x, z); wasMoved(z)
if isAtom(ri[1]):
var snk = c.genSink(s, dest, ri, flags)
var snk = c.genSink(dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
else:
result = genFieldAccessSideEffects(c, s, dest, ri, flags)
result = genFieldAccessSideEffects(c, dest, ri, flags)
else:
result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags)
result = c.genSink(dest, destructiveMoveVar(ri, c, s), flags)
else:
inc c.inEnsureMove, isEnsureMove
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
c.finishCopy(result, dest, isFromSink = false)
of nkBracket:
# array constructor
if ri.len > 0 and isDangerousSeq(ri.typ):
inc c.inEnsureMove, isEnsureMove
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
c.finishCopy(result, dest, isFromSink = false)
else:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
result = c.genSink(dest, p(ri, c, s, consumed), flags)
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
result = c.genSink(dest, p(ri, c, s, consumed), flags)
of nkSym:
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
let snk = c.genSink(dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
elif ri.sym.kind != skParam and
isAnalysableFieldAccess(ri, c.owner) and
isLastRead(ri, c, s) and canBeMoved(c, dest.typ):
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
let snk = c.genSink(dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
else:
inc c.inEnsureMove, isEnsureMove
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
c.finishCopy(result, dest, isFromSink = false)
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
if IsExplicitSink in flags:
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
else:
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
result = c.genSink(dest, p(ri, c, s, sinkArg), flags)
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
# We know the result will be a stmt so we use that fact to optimize
handleNestedTempl(ri, process, willProduceStmt = true)
of nkRaiseStmt:
result = pRaiseStmt(ri, c, s)
else:
let isOwnsData = ownsData(c, s, ri2, flags)
if isOwnsData != nil:
result = moveOrCopy(dest, isOwnsData, c, s, flags)
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
canBeMoved(c, dest.typ):
# Rule 3: `=sink`(x, z); wasMoved(z)
let snk = c.genSink(s, dest, ri, flags)
let snk = c.genSink(dest, ri, flags)
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
else:
inc c.inEnsureMove, isEnsureMove
result = c.genCopy(dest, ri, flags)
dec c.inEnsureMove, isEnsureMove
result.add p(ri, c, s, consumed)
c.finishCopy(result, dest, flags, isFromSink = false)
c.finishCopy(result, dest, isFromSink = false)
when false:
proc computeUninit(c: var Con) =
@@ -1329,55 +1097,6 @@ when false:
for i in 0..<n.safeLen:
injectDefaultCalls(n[i], c)
proc replaceSinkParam(n: PNode, mapping: Table[int, PSym]): PNode =
case n.kind
of nkSym:
if n.sym.id in mapping:
result = newSymNode(mapping[n.sym.id])
else:
result = n
of nkVarSection, nkLetSection:
result = copyNode(n)
newSons(result, n.len)
for i in 0..<n.len:
result[i] = copyNode(n[i])
for j in 0..<n[i].len-1:
result[i].add n[i][j]
result[i].add replaceSinkParam(n[i][^1], mapping)
of {nkNone..nkNilLit}-{nkSym}, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
result = n
else:
result = copyNode(n)
for i in 0..<n.len:
result.add replaceSinkParam(n[i], mapping)
proc addSinkCopy(c: var Con; s: var Scope; sinkParams: seq[PSym]; n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
var mapping = initTable[int, PSym]()
var mutated = newSeq[PNode]()
getPotentialWrites(n, false, mutated)
var mutatedSet = initIntSet()
for m in mutated:
mutatedSet.incl m.sym.id
for param in sinkParams:
if param.id in mutatedSet:
let newSym = newSym(skTemp, getIdent(c.graph.cache, "sinkCopy"), c.idgen, param.owner, n.info)
newSym.flagsImpl.incl sfFromGeneric
newSym.typ = param.typ.elementType
mapping[param.id] = newSym
let v = newNodeI(nkVarSection, n.info)
v.addVar(newSymNode(newSym), newSymNode(param))
result.add v
if mapping.len > 0:
result.add replaceSinkParam(n, mapping)
else:
result = n
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
when toDebug.len > 0:
shouldDebug = toDebug == owner.name.s or toDebug == "always"
@@ -1391,24 +1110,15 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n:
var scope = Scope(body: n)
let body = p(n, c, scope, normal)
var sinkParams = newSeq[PSym]()
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
let params = owner.typ.n
for i in 1..<params.len:
let t = params[i].sym.typ
if isSinkTypeForParam(t):
let baseType = t.skipTypes({tySink})
if baseType.kind in {tyString, tySequence, tyArray, tyTuple, tyObject}:
sinkParams.add params[i].sym
if hasDestructor(c, baseType):
scope.final.add c.genDestroy(params[i])
if isSinkTypeForParam(t) and hasDestructor(c, t.skipTypes({tySink})):
scope.final.add c.genDestroy(params[i])
#if optNimV2 in c.graph.config.globalOptions:
# injectDefaultCalls(n, c)
result = optimize processScope(c, scope, body)
if sinkParams.len > 0:
result = addSinkCopy(c, scope, sinkParams, result)
dbg:
echo ">---------transformed-to--------->"
echo renderTree(result, {renderIds})

View File

@@ -1,122 +0,0 @@
proc copySymdef(n: PNode; locals: var Table[int, PSym]; idgen: IdGenerator; owner: PSym): PNode =
case n.kind
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
result = n
of nkSym:
let oldSym = n.sym
let newSym = copySym(oldSym, idgen)
setOwner(newSym, owner)
locals[oldSym.id] = newSym
result = newSymNode(newSym, oldSym.info)
else:
result = shallowCopy(n)
for i in 0..<n.len:
result[i] = copySymdef(n[i], locals, idgen, owner)
proc copyInlineProcBody(n: PNode; locals: var Table[int, PSym]; idgen: IdGenerator; owner: PSym): PNode =
case n.kind
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
result = n
of nkSym:
let sym = locals.getOrDefault(n.sym.id)
if sym != nil:
result = newSymNode(sym, n.info)
else:
result = n
of nkLetSection, nkVarSection:
result = shallowCopy(n)
for i in 0..<n.len:
let it = n[i]
if it.kind == nkCommentStmt:
result[i] = it
elif it.kind in {nkIdentDefs, nkConstDef}:
result[i] = shallowCopy(it)
for j in 0..<it.len-2:
result[i][j] = copySymdef(it[j], locals, idgen, owner)
for j in it.len-2..<it.len:
result[i][j] = copyInlineProcBody(it[j], locals, idgen, owner)
else:
assert it.kind == nkVarTuple
result[i] = shallowCopy(it)
for j in 0..<it.len-2:
assert it[j].kind == nkSym
let oldSym = it[j].sym
let newSym = copySym(oldSym, idgen)
setOwner(newSym, owner)
locals[oldSym.id] = newSym
result[i][j] = newSymNode(newSym, oldSym.info)
for j in it.len-2..<it.len:
result[i][j] = copyInlineProcBody(it[j], locals, idgen, owner)
of nkForStmt, nkParForStmt:
result = shallowCopy(n)
for i in 0..<n.len-2:
assert n[i].kind == nkSym
let oldSym = n[i].sym
let newSym = copySym(oldSym, idgen)
setOwner(newSym, owner)
locals[oldSym.id] = newSym
result[i] = newSymNode(newSym, oldSym.info)
result[n.len-2] = copyInlineProcBody(n[n.len-2], locals, idgen, owner)
result[n.len-1] = copyInlineProcBody(n[n.len-1], locals, idgen, owner)
of routineDefs, nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt, nkConstSection:
result = n
else:
result = shallowCopy(n)
for i in 0..<n.len:
result[i] = copyInlineProcBody(n[i], locals, idgen, owner)
proc copyParams(n: PNode; locals: var Table[int, PSym]; idgen: IdGenerator; owner: PSym): PNode =
result = shallowCopy(n)
result[0] = n[0] # return type
for i in 1..<n.len:
let it = n[i]
assert it.kind == nkIdentDefs
result[i] = shallowCopy(it)
for j in 0..<it.len-2:
assert it[j].kind == nkSym
let oldSym = it[j].sym
let newSym = copySym(oldSym, idgen)
setOwner(newSym, owner)
locals[oldSym.id] = newSym
result[i][j] = newSymNode(newSym, oldSym.info)
owner.typ.addParam newSym
for j in it.len-2..<it.len:
result[i][j] = copyInlineProcBody(it[j], locals, idgen, owner)
proc copyInlineProc(prc: PSym; idgen: IdGenerator): PSym =
result = copySym(prc, idgen)
var locals = initTable[int, PSym]()
var a = shallowCopy(prc.ast)
if resultPos < prc.ast.len and prc.ast[resultPos].kind == nkSym:
let oldRes = prc.ast[resultPos].sym
let newRes = copySym(oldRes, idgen)
setOwner(newRes, result)
locals[oldRes.id] = newRes
a[resultPos] = newSymNode(newRes, oldRes.info)
result.typ = copyType(prc.typ, idgen, result)
result.typ.n = newNodeI(prc.typ.n.kind, prc.typ.n.info)
if prc.typ.n.len > 0:
result.typ.n.add copyNode(prc.typ.n[0])
for i in 1..<prc.typ.n.len:
let it = prc.typ.n[i]
assert it.kind == nkSym
let oldSym = it.sym
let newSym = copySym(oldSym, idgen)
setOwner(newSym, result)
locals[oldSym.id] = newSym
result.typ.addParam newSym
for i in 0..<prc.ast.len:
if i == paramsPos:
a[i] = copyTree(prc.ast[i])
elif i == resultPos and prc.ast[i].kind == nkSym:
discard "handled above"
else:
a[i] = copyInlineProcBody(prc.ast[i], locals, idgen, result)
result.ast = a
#echo "Produced: ", renderTree(result.ast, {renderIds})

View File

@@ -6,11 +6,11 @@ Name: "Nim"
Version: "$version"
Platforms: """
windows: i386;amd64
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;s390x;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
macosx: i386;amd64;powerpc64;arm64
solaris: i386;amd64;sparc;sparc64
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
netbsd: i386;amd64;arm64
netbsd: i386;amd64
openbsd: i386;amd64;arm;arm64
dragonfly: i386;amd64
crossos: amd64
@@ -65,13 +65,11 @@ Files: "compiler"
Files: "doc"
Files: "doc/html"
Files: "tools"
Files: "tools/debug/nim-gdb.py"
Files: "tools/nim-gdb.py"
Files: "nimpretty"
Files: "testament"
Files: "nimsuggest"
Files: "nimsuggest/tests/*.nim"
Files: "changelogs/*.md"
Files: "ci/funs.sh"
[Lib]
Files: "lib"
@@ -79,8 +77,6 @@ Files: "lib"
[Other]
Files: "examples"
Files: "dist/nimble"
Files: "dist/checksums"
Files: "dist/nimony"
Files: "tests"
@@ -94,7 +90,6 @@ Files: "bin/nimgrab.exe"
Files: "bin/nimpretty.exe"
Files: "bin/testament.exe"
Files: "bin/nim-gdb.bat"
Files: "bin/atlas.exe"
Files: "koch.exe"
Files: "finish.exe"
@@ -125,7 +120,6 @@ Files: "bin/nim"
InstallScript: "yes"
UninstallScript: "yes"
Files: "bin/nim-gdb"
Files: "build_all.sh"
[InnoSetup]
@@ -148,4 +142,4 @@ licenses: "bin/nim,MIT;lib/*,MIT;"
[nimble]
pkgName: "nim"
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css;doc/nimdoc.cls"
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"

View File

@@ -3,7 +3,7 @@
## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
## type is for that purpose.
from std/math import trunc
from math import trunc
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -33,28 +33,26 @@ template high*(t: typedesc[Int128]): Int128 = Max
proc `$`*(a: Int128): string
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
{.noSideEffect.}:
result = Zero
when T is bool: result.sdata(0) = int32(arg)
elif 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)
elif 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
when T is bool: result.sdata(0) = int32(arg)
elif T is SomeUnsignedInt:
when sizeof(arg) <= 4:
result.udata[0] = uint32(arg)
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
result.udata[0] = uint32(arg and T(0xffffffff))
result.udata[1] = uint32(arg shr 32)
elif 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
@@ -172,7 +170,6 @@ proc addToHex*(result: var string; arg: Int128) =
i -= 1
proc toHex*(arg: Int128): string =
result = ""
result.addToHex(arg)
proc inc*(a: var Int128, y: uint32 = 1) =
@@ -209,35 +206,30 @@ proc `==`*(a, b: Int128): bool =
return true
proc bitnot*(a: Int128): Int128 =
result = Zero
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 = Zero
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 = Zero
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 = Zero
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 =
result = Zero
let b = b and 127
if b < 32:
result.sdata(3) = a.sdata(3) shr b
@@ -264,7 +256,6 @@ proc `shr`*(a: Int128, b: int): Int128 =
result.sdata(0) = a.sdata(3) shr (b and 31)
proc `shl`*(a: Int128, b: int): Int128 =
result = Zero
let b = b and 127
if b < 32:
result.udata[0] = a.udata[0] shl b
@@ -288,7 +279,6 @@ proc `shl`*(a: Int128, b: int): Int128 =
result.udata[3] = a.udata[0] shl (b and 31)
proc `+`*(a, b: Int128): Int128 =
result = Zero
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)
@@ -321,7 +311,6 @@ proc abs(a: int32): int =
if a < 0: -a else: a
proc `*`(a: Int128, b: uint32): Int128 =
result = Zero
let tmp0 = uint64(a.udata[0]) * uint64(b)
let tmp1 = uint64(a.udata[1]) * uint64(b)
let tmp2 = uint64(a.udata[2]) * uint64(b)
@@ -340,11 +329,10 @@ proc `*`*(a: Int128, b: int32): Int128 =
if b < 0:
result = -result
proc `*=`(a: var Int128, b: int32) =
a = a * b
proc `*=`*(a: var Int128, b: int32): Int128 =
result = result * b
proc makeInt128(high, low: uint64): Int128 =
result = Zero
result.udata[0] = cast[uint32](low)
result.udata[1] = cast[uint32](low shr 32)
result.udata[2] = cast[uint32](high)
@@ -368,10 +356,9 @@ proc `*`*(lhs, rhs: Int128): Int128 =
proc `*=`*(a: var Int128, b: Int128) =
a = a * b
import std/bitops
import bitops
proc fastLog2*(a: Int128): int =
result = 0
if a.udata[3] != 0:
return 96 + fastLog2(a.udata[3])
if a.udata[2] != 0:
@@ -383,8 +370,6 @@ proc fastLog2*(a: Int128): int =
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
assert(divisor != Zero)
result = (Zero, Zero)
let isNegativeA = isNegative(dividend)
let isNegativeB = isNegative(divisor)
@@ -460,9 +445,7 @@ proc addInt128*(result: var string; value: Int128) =
var i = initialSize
var j = high(result)
while i < j:
let tmp = result[i]
result[i] = result[j]
result[j] = tmp
swap(result[i], result[j])
i += 1
j -= 1
@@ -553,28 +536,24 @@ proc toInt128*(arg: float64): Int128 =
return res
proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0]
result.udata[1] = arg.udata[1]
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0]
result.udata[1] = 0
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0] and 0xffff
result.udata[1] = 0
result.udata[2] = 0
result.udata[3] = 0
proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
result = Zero
result.udata[0] = arg.udata[0] and 0xff
result.udata[1] = 0
result.udata[2] = 0
@@ -591,4 +570,4 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
of 8:
return maskUInt64(arg)
else:
raiseAssert "masking only implemented for 1, 2, 4 and 8 bytes"
assert(false, "masking only implemented for 1, 2, 4 and 8 bytes")

View File

@@ -11,9 +11,7 @@
## https://github.com/nim-lang/RFCs/issues/244 for more details.
import
ast, types, renderer
import std/intsets
ast, types, renderer, intsets
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -23,7 +21,6 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
case n.kind
of nkRecList:
result = false
for i in 0..<n.len:
result = canAliasN(arg, n[i], marker)
if result: return
@@ -39,7 +36,7 @@ proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
else: discard
of nkSym:
result = canAlias(arg, n.sym.typ, marker)
else: result = false
else: discard
proc canAlias(arg, ret: PType; marker: var IntSet): bool =
if containsOrIncl(marker, ret.id):
@@ -54,18 +51,17 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool =
of tyObject:
if isFinal(ret):
result = canAliasN(arg, ret.n, marker)
if not result and ret.baseClass != nil:
result = canAlias(arg, ret.baseClass, marker)
if not result and ret.len > 0 and ret[0] != nil:
result = canAlias(arg, ret[0], marker)
else:
result = true
of tyTuple:
result = false
for r in ret.kids:
result = canAlias(arg, r, marker)
for i in 0..<ret.len:
result = canAlias(arg, ret[i], marker)
if result: break
of tyArray, tySequence, tyDistinct, tyGenericInst,
tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
result = canAlias(arg, ret.skipModifier, marker)
result = canAlias(arg, ret.lastSon, marker)
of tyProc:
result = ret.callConv == ccClosure
else:
@@ -76,71 +72,6 @@ proc isValueOnlyType(t: PType): bool =
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
result = not types.searchTypeFor(t, wrap)
type
SearchResult = enum
NotFound, Abort, Found
proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult
proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult =
result = NotFound
case n.kind
of nkRecList:
for i in 0..<n.len:
result = containsDangerousRefAux(n[i], marker)
if result == Found: return result
of nkRecCase:
assert(n[0].kind == nkSym)
result = containsDangerousRefAux(n[0], marker)
if result == Found: return result
for i in 1..<n.len:
case n[i].kind
of nkOfBranch, nkElse:
result = containsDangerousRefAux(lastSon(n[i]), marker)
if result == Found: return result
else: discard
of nkSym:
result = containsDangerousRefAux(n.sym.typ, marker)
else: discard
proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
result = NotFound
if t == nil: return result
if containsOrIncl(marker, t.id): return result
if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure):
result = Found
elif tfSendable in t.flags:
result = Abort
else:
# continue the type traversal:
result = NotFound
if result != NotFound: return result
case t.kind
of tyObject:
if t.baseClass != nil:
result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker)
if result == NotFound: result = containsDangerousRefAux(t.n, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = containsDangerousRefAux(skipModifier(t), marker)
of tyArray, tySet, tySequence:
result = containsDangerousRefAux(t.elementType, marker)
of tyTuple:
for a in t.kids:
result = containsDangerousRefAux(a, marker)
if result == Found: return result
else:
discard
proc containsDangerousRef(t: PType): bool =
# a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`.
# For example:
# `ref int` # dangerous
# `Isolated[ref int]` # not dangerous
var marker = initIntSet()
result = containsDangerousRefAux(t, marker) == Found
proc canAlias*(arg, ret: PType): bool =
if isValueOnlyType(arg):
# can alias only with addr(arg.x) and we don't care if it is not safe
@@ -149,15 +80,12 @@ proc canAlias*(arg, ret: PType): bool =
var marker = initIntSet()
result = canAlias(arg, ret, marker)
const
SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
proc containsVariable(n: PNode): bool =
case n.kind
of nodesToIgnoreSet:
result = false
of nkSym:
result = n.sym.kind in SomeVar
result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
else:
for ch in n:
if containsVariable(ch): return true
@@ -166,7 +94,7 @@ proc containsVariable(n: PNode): bool =
proc checkIsolate*(n: PNode): bool =
if types.containsTyRef(n.typ):
# XXX Maybe require that 'n.typ' is acyclic. This is not much
# worse than the already existing inheritance and closure restrictions.
# worse than the already exisiting inheritance and closure restrictions.
case n.kind
of nkCharLit..nkNilLit:
result = true
@@ -181,7 +109,7 @@ proc checkIsolate*(n: PNode): bool =
discard "fine, it is isolated already"
else:
let argType = n[i].typ
if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
if argType.canAlias(n.typ) or containsVariable(n[i]):
# bug #19013: Alias information is not enough, we need to check for potential
# "overlaps". I claim the problem can only happen by reading again from a location
@@ -190,12 +118,10 @@ proc checkIsolate*(n: PNode): bool =
return false
result = true
of nkIfStmt, nkIfExpr:
result = false
for it in n:
result = checkIsolate(it.lastSon)
if not result: break
of nkCaseStmt:
result = false
for i in 1..<n.len:
result = checkIsolate(n[i].lastSon)
if not result: break
@@ -205,7 +131,6 @@ proc checkIsolate*(n: PNode): bool =
result = checkIsolate(n[i].lastSon)
if not result: break
of nkBracket, nkTupleConstr, nkPar:
result = false
for it in n:
result = checkIsolate(it)
if not result: break
@@ -218,12 +143,6 @@ proc checkIsolate*(n: PNode): bool =
result = checkIsolate(n[^1])
else:
result = false
of nkSym:
result = true
if n.sym.kind in SomeVar:
let argType = n.typ
if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
result = false
else:
# unanalysable expression:
result = false

View File

@@ -1,98 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2026 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## `ItemId` is the identity of a symbol or type: a `(module, item)` pair.
##
## The fields are private on purpose: the module half reserves bit 30 as the
## "backend minted" marker, so all construction and inspection has to go
## through this module's API and the marker bit can never leak into module
## indexing or arithmetic.
##
## Three id spaces coexist per module:
## - Semantic-phase and NIF-loader ids: `itemId(module, item)` with `item > 0`.
## - Backend-minted ids (IC codegen, `nim nifc`: transf labels and temps,
## lifted hooks): `backendItemId` sets `BackendModuleBit`, so these can
## never compare equal to a loader id even though both counters mint the
## same small `item` range in one process. They never cross a process
## boundary and must never be written to a NIF file.
## - Derived env/tuple-field ids (`lowerings.addField`): the source local's
## id with `item` negated. `derivedFieldId` preserves the backend marker,
## keeping the derivation collision-free for both id spaces above.
import std/hashes
when defined(nimPreviewSlimSystem):
import std/assertions
const
BackendModuleBit = 0x4000_0000'i32
# Bit 30 of the module field. Bit 31 stays clear so marked module values
# remain non-negative and cannot be mistaken for the special negative
# module ids like `PackageModuleId`.
PackageModuleId* = -3'i32
type
ItemId* = object
moduleBits: int32
itemBits: int32
proc itemId*(module, item: int32): ItemId {.inline.} =
assert module < 0 or (module and BackendModuleBit) == 0
ItemId(moduleBits: module, itemBits: item)
proc backendItemId*(module, item: int32): ItemId {.inline.} =
## An id minted during IC codegen; distinct from every `itemId` of the
## same module so that the loader's stub counter and the backend's counter
## cannot collide in id-keyed tables.
assert module >= 0 and (module and BackendModuleBit) == 0
ItemId(moduleBits: module or BackendModuleBit, itemBits: item)
proc module*(x: ItemId): int32 {.inline.} =
if x.moduleBits >= 0: x.moduleBits and not BackendModuleBit
else: x.moduleBits
proc item*(x: ItemId): int32 {.inline.} = x.itemBits
proc isBackendMinted*(x: ItemId): bool {.inline.} =
x.moduleBits >= 0 and (x.moduleBits and BackendModuleBit) != 0
proc derivedFieldId*(source: ItemId): ItemId {.inline.} =
## The id of the env/tuple field that `lowerings.addField` derives for a
## captured local: `item` negated, module bits (including the backend
## marker) preserved.
ItemId(moduleBits: source.moduleBits, itemBits: -abs(source.itemBits))
proc matchesDerivedFieldId*(field, source: ItemId): bool {.inline.} =
## Does `field` carry the id `derivedFieldId` would derive for `source`?
## `source` may itself already be the derived field id.
field.moduleBits == source.moduleBits and
field.itemBits == -abs(source.itemBits)
proc `==`*(a, b: ItemId): bool {.inline.} =
# raw bit comparison: a backend-minted id never equals a loader id
a.itemBits == b.itemBits and a.moduleBits == b.moduleBits
proc hash*(x: ItemId): Hash =
var h: Hash = hash(x.moduleBits)
h = h !& hash(x.itemBits)
result = !$h
proc `$`*(x: ItemId): string =
result = "(module: " & $x.module & ", item: " & $x.itemBits
if x.isBackendMinted: result.add ", backend"
result.add ")"
const
moduleShift = when defined(cpu32): 20 else: 24
proc toId*(a: ItemId): int {.inline.} =
## Packs an ItemId into a single int. Uses the raw module bits so the
## backend marker keeps the two id spaces disjoint (bit 30 shifts to
## bit 54; like the module/item split itself this needs a 64-bit int).
(a.moduleBits.int shl moduleShift) + a.itemBits.int

File diff suppressed because it is too large Load Diff

View File

@@ -69,7 +69,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
else: internalError(p.config, n.info, "genObjectFields")
proc objHasTypeField(t: PType): bool {.inline.} =
tfInheritable in t.flags or t.baseClass != nil
tfInheritable in t.flags or t[0] != nil
proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
let kind = if objHasTypeField(typ): tyObject else: tyTuple
@@ -79,9 +79,9 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
p.g.typeInfo.addf("var NNI$1 = $2;$n",
[rope(typ.id), genObjectFields(p, typ, typ.n)])
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
if (typ.kind == tyObject) and (typ.baseClass != nil):
if (typ.kind == tyObject) and (typ[0] != nil):
p.g.typeInfo.addf("$1.base = $2;$n",
[name, genTypeInfo(p, typ.baseClass.skipTypes(skipPtrs))])
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
proc genTupleFields(p: PProc, typ: PType): Rope =
var s: Rope = ""
@@ -117,40 +117,40 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
prepend(p.g.typeInfo, s)
p.g.typeInfo.add(n)
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
if typ.baseClass != nil:
if typ[0] != nil:
p.g.typeInfo.addf("$1.base = $2;$n",
[name, genTypeInfo(p, typ.baseClass)])
[name, genTypeInfo(p, typ[0])])
proc genTypeInfo(p: PProc, typ: PType): Rope =
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned} + tyUserTypeClasses)
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind
of tyDistinct:
result = genTypeInfo(p, t.skipModifier)
result = genTypeInfo(p, t[0])
of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray:
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
var s =
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
p.g.typeInfo.addf("$1.base = $2;$n",
[result, genTypeInfo(p, t.elementType)])
[result, genTypeInfo(p, t.lastSon)])
of tyArray:
var s =
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
p.g.typeInfo.addf("$1.base = $2;$n",
[result, genTypeInfo(p, t.elementType)])
[result, genTypeInfo(p, t[1])])
of tyEnum: genEnumInfo(p, t, result)
of tyObject: genObjectInfo(p, t, result)
of tyTuple: genTupleInfo(p, t, result)
of tyStatic:
if t.n != nil: result = genTypeInfo(p, skipModifier t)
if t.n != nil: result = genTypeInfo(p, lastSon t)
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')

View File

@@ -10,12 +10,10 @@
# This file implements lambda lifting for the transformator.
import
options, ast, astalgo, msgs,
idents, renderer, types, magicsys, lowerings, modulegraphs, lineinfos,
intsets, strutils, options, ast, astalgo, msgs,
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
transf, liftdestructors, typeallowed
import std/[strutils, tables, intsets]
when defined(nimPreviewSlimSystem):
import std/assertions
@@ -135,50 +133,35 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator)
var n = newNodeI(nkRange, iter.info)
n.add newIntNode(nkIntLit, -1)
n.add newIntNode(nkIntLit, 0)
result = newType(tyRange, idgen, iter)
result = newType(tyRange, nextTypeId(idgen), iter)
result.n = n
var intType = nilOrSysInt(g)
if intType.isNil: intType = newType(tyInt, idgen, iter)
if intType.isNil: intType = newType(tyInt, nextTypeId(idgen), iter)
rawAddSon(result, intType)
proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info)
result.typ = createClosureIterStateType(g, iter, idgen)
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
result = createObj(g, idgen, owner, info, final=false)
result.incl tfFinal
if owner.isIterator:
rawAddField(result, createStateField(g, owner, idgen))
rawAddField(result, createStateField(g, owner, idgen))
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
if resultPos < iter.ast.len:
result = iter.ast[resultPos].sym
else:
# XXX a bit hacky:
result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
result.typ = iter.typ.returnType
incl(result.flagsImpl, sfUsed)
result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {})
result.typ = iter.typ[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
proc closureParams(routine: PSym): PNode =
## The formal parameters node lambda lifting reads and extends. In a
## from-source compilation `routine.ast[paramsPos]` and `routine.typ.n` are the
## very same node (see the `typ.n.len` based position math below). Under IC the
## loaded proc AST omits the parameters (they are kept only in `typ.n`), so
## restore the shared node here.
result = routine.ast[paramsPos]
if (result == nil or result.kind == nkEmpty) and routine.typ != nil and
routine.typ.n != nil and routine.ast.len > paramsPos:
result = routine.typ.n
routine.ast[paramsPos] = result
proc addHiddenParam*(routine: PSym, param: PSym) =
proc addHiddenParam(routine: PSym, param: PSym) =
assert param.kind == skParam
var params = closureParams(routine)
var params = routine.ast[paramsPos]
# -1 is correct here as param.position is 0 based but we have at position 0
# some nkEffect node:
param.position = routine.typ.n.len-1
@@ -187,24 +170,24 @@ proc addHiddenParam*(routine: PSym, param: PSym) =
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
proc getEnvParam*(routine: PSym): PSym =
if routine.ast.isNil: return nil
let params = closureParams(routine)
if params == nil or params.len == 0: return nil
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
else:
result = nil
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
result = getEnvParam(routine)
if result.isNil:
# writeStackTrace()
localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
result = routine
proc getEnvParam*(routine: PSym): PSym =
let params = routine.ast[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.name.s == paramName:
result = hidden.sym
assert sfFromGeneric in result.flags
proc interestingVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
sfGlobal notin s.flags and
@@ -213,11 +196,9 @@ proc interestingVar(s: PSym): bool {.inline.} =
proc illegalCapture(s: PSym): bool {.inline.} =
result = classifyViewType(s.typ) != noView or s.kind == skResult
proc isInnerProc*(s: PSym): bool =
proc isInnerProc(s: PSym): bool =
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
result = s.skipGenericOwner.kind in routineKinds
else:
result = false
proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
# Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
@@ -229,10 +210,6 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
result[0] = le
result[1] = ri
proc markInjectDestructors(s: PSym) {.inline.} =
backendEnsureMutable s
s.flagsImpl.incl sfInjectDestructors
proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode =
result = newNodeIT(nkClosure, info, prc.typ)
result.add(newSymNode(prc))
@@ -245,19 +222,28 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
#if isClosureIterator(result.typ):
createTypeBoundOps(g, nil, result.typ, info, idgen)
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
markInjectDestructors(prc)
prc.flags.incl sfInjectDestructors
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
proc interestingIterVar(s: PSym): bool {.inline.} =
# XXX optimization: Only lift the variable if it lives across
# yield/return boundaries! This can potentially speed up
# closure iterators quite a bit.
result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime
result = conf.backend == backendJs and not isCompileTime
proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
if owner.kind != skMacro:
createTypeBoundOps(g, nil, refType.elementType, info, idgen)
createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
createTypeBoundOps(g, nil, refType, info, idgen)
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
markInjectDestructors(owner)
owner.flags.incl sfInjectDestructors
proc genCreateEnv(env: PNode): PNode =
var c = newNodeIT(nkObjConstr, env.info, env.typ)
@@ -272,7 +258,8 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
let iter = n.sym
assert iter.isIterator
result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let hp = getHiddenParam(g, iter)
var env: PNode
if owner.isIterator:
@@ -280,7 +267,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
env = indirectAccess(newSymNode(it), hp, hp.info)
else:
let e = newSym(skLet, iter.name, idgen, owner, n.info)
let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info)
e.typ = hp.typ
e.flags = hp.flags
env = newSymNode(e)
@@ -293,58 +280,46 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
result.add makeClosure(g, idgen, iter, env, n.info)
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
let field = addField(obj, s, g.cache, idgen)
var access = newSymNode(envParam)
assert obj.kind == tyObject
result = rawIndirectAccess(access, field, s.info)
# ------------------ new stuff -------------------------------------------
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
let s = n.sym
let isEnv = s.name.id == getIdent(g.cache, ":env").id
if illegalCapture(s):
localError(g.config, n.info,
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
" Consider using a <ref T> which can be captured.") %
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
" Consider using a <ref $2> which can be captured.") %
[s.name.s, typeToString(s.typ), g.config$s.info])
elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
[s.name.s, owner.name.s, $owner.typ.callConv])
unsealForTransform(owner.typ)
incl(owner.typ, tfCapturesEnv)
# A closure proc type that captures an env owns a REF to it: copying the closure
# value must incref the env and destroying it must decref. That is exactly what
# `tfHasAsgn` signals to `injectDestructorCalls` (so a closure assignment becomes
# `=copy`, not a raw field store).
#
# Set it HERE (closure-type creation) so the flag is DETERMINISTIC and serializes
# with the type — but ONLY under `nim ic`. The per-module `lower` stage is a
# separate process that lowers routines in index order; if a consumer (e.g.
# `workNimAsyncContinue`) was lowered before the closure type's ops were lifted,
# its env store emitted a RAW assign with no incref → freed env → async
# "yielded `nil`". A normal single-process `nim c` build does NOT need this —
# `createTypeBoundOps` sets the flag lazily, in lift order, before it matters
# (the old `liftdestructors ~1498` "XXX Breaks IC!" side effect) — and setting it
# eagerly there REGRESSES codegen: a `=destroy` hook gets generated against the
# bare `void(*)(void)` proc representation but is then called with closure structs
# (`eqdestroy__u2__stdZtypedthreads` type mismatch — broke megatest). So gate on
# `cmdNifC`; normal builds keep the lazy (devel) behavior.
if g.config.cmd == cmdNifC:
incl(owner.typ, tfHasAsgn)
if not isEnv:
owner.typ.callConv = ccClosure
incl(owner.typ.flags, tfCapturesEnv)
owner.typ.callConv = ccClosure
type
DetectionPass = object
processed, capturedVars: IntSet
ownerToType: Table[int, PType]
somethingToDo: bool
inTypeOf: bool
graph: ModuleGraph
idgen: IdGenerator
proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
result = DetectionPass(processed: toIntSet([fn.id]),
capturedVars: initIntSet(), ownerToType: initTable[int, PType](),
graph: g, idgen: idgen
)
result.processed = initIntSet()
result.capturedVars = initIntSet()
result.ownerToType = initTable[int, PType]()
result.processed.incl(fn.id)
result.graph = g
result.idgen = idgen
discard """
proc outer =
@@ -360,20 +335,16 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
let env = getEnvParam(owner)
if env.isNil or not owner.isIterator:
result = newType(tyRef, c.idgen, owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
else:
result = env.typ
result = newType(tyRef, nextTypeId(c.idgen), owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
if optOwnedRefs in c.graph.config.globalOptions:
assert t.kind == tyRef
result = newType(tyOwned, c.idgen, t.owner)
result.incl tfHasOwned
result = newType(tyOwned, nextTypeId(c.idgen), t.owner)
result.flags.incl tfHasOwned
result.rawAddSon t
else:
result = t
@@ -381,7 +352,7 @@ proc asOwnedRef(c: var DetectionPass; t: PType): PType =
proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
var r = c.getEnvTypeForOwner(owner, info)
result = newType(tyPtr, c.idgen, owner)
result = newType(tyPtr, nextTypeId(c.idgen), owner)
rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
@@ -409,7 +380,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
else:
let result = newSym(skField, upIdent, c.idgen, obj.owner, obj.owner.info)
let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info)
result.typ = fieldType
when false:
if c.graph.config.selectedGC == gcDestructors:
@@ -442,40 +413,19 @@ Consider:
"""
proc isTypeOf(n: PNode): bool =
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
proc isEnvTypeForRoutine(envTyp: PType; routine: PSym): bool =
## True if `envTyp` is (maybe wrapped) env object type owned by `routine`, as
## created by `getEnvTypeForOwner` / `createEnvObj`.
let obj = envTyp.skipTypes({tyOwned, tyRef, tyPtr})
result = obj.kind == tyObject and obj.owner.id == routine.id
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
var cp = getEnvParam(fn)
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
let t = c.getEnvTypeForOwner(owner, info)
if cp == nil:
cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info)
incl(cp.flagsImpl, sfFromGeneric)
cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info)
incl(cp.flags, sfFromGeneric)
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
# Nested `liftLambdas` uses a fresh `DetectionPass`, so `getEnvTypeForOwner`
# can allocate another PType for the same logical env; the hidden param from
# the inner pass is authoritative (bug #21242).
if isEnvTypeForRoutine(cp.typ, owner) and isEnvTypeForRoutine(t, owner):
c.ownerToType[owner.id] = cp.typ
else:
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
let cp = getEnvParam(iter)
doAssert(cp != nil, "Env param not present in iter")
let upField = lookupInRecord(cp.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
upField != nil
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
case n.kind
of nkSym:
@@ -491,17 +441,24 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
if innerProc:
if s.isIterator: c.somethingToDo = true
if not c.processed.containsOrIncl(s.id):
let body = transformBody(c.graph, c.idgen, s, {useCache})
let body = transformBody(c.graph, c.idgen, s, useCache)
detectCapturedVars(body, s, c)
let ow = s.skipGenericOwner
let innerClosure = innerProc and s.typ.callConv == ccClosure and (not s.isIterator or iterEnvHasUpField(c.graph, s))
let interested = interestingVar(s)
if ow == owner:
if owner.isIterator:
c.somethingToDo = true
addClosureParam(c, owner, n.info)
if interestingIterVar(s):
if not c.capturedVars.containsOrIncl(s.id):
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
if s.name.id == getIdent(c.graph.cache, ":state").id:
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
else:
discard addField(obj, s, c.graph.cache, c.idgen)
# direct or indirect dependency:
elif innerClosure or interested:
elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s):
discard """
proc outer() =
var x: int
@@ -518,12 +475,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
addClosureParam(c, owner, n.info)
#echo "capturing ", n.info
# variable 's' is actually captured:
if interestingVar(s):
if not c.capturedVars.contains(s.id):
if not c.inTypeOf: c.capturedVars.incl(s.id)
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
discard addField(obj, s, c.graph.cache, c.idgen)
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
discard addField(obj, s, c.graph.cache, c.idgen)
# create required upFields:
var w = owner.skipGenericOwner
if isInnerProc(w) or owner.isIterator:
@@ -555,14 +510,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
detectCapturedVars(n[namePos], owner, c)
of nkReturnStmt:
detectCapturedVars(n[0], owner, c)
of nkIdentDefs:
detectCapturedVars(n[^1], owner, c)
else:
if n.isCallExpr and n[0].isTypeOf:
c.inTypeOf = true
for i in 0..<n.len:
detectCapturedVars(n[i], owner, c)
c.inTypeOf = false
type
LiftingPass = object
@@ -572,8 +522,9 @@ type
unownedEnvVars: Table[int, PNode] # only required for --newruntime
proc initLiftingPass(fn: PSym): LiftingPass =
result = LiftingPass(processed: toIntSet([fn.id]),
envVars: initTable[int, PNode]())
result.processed = initIntSet()
result.processed.incl(fn.id)
result.envVars = initTable[int, PNode]()
proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let s = n.sym
@@ -581,8 +532,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
if not envParam.isNil:
var access = newSymNode(envParam)
var obj = access.typ.elementType
while true:
let obj = access.typ[0]
assert obj.kind == tyObject
let field = getFieldFromObj(obj, s)
if field != nil:
@@ -590,19 +541,18 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
if upField == nil: break
access = rawIndirectAccess(access, upField, n.info)
obj = access.typ.baseClass
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
result = n
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
var v = newSym(skVar, getIdent(cache, envName), idgen, owner, info)
var v = newSym(skVar, getIdent(cache, envName), nextSymId(idgen), owner, info)
v.flags = {sfShadowed, sfGeneratedOp}
v.typ = typ
result = newSymNode(v)
when false:
if owner.kind == skIterator and owner.typ.callConv == ccClosure:
let it = getHiddenParam(owner)
addUniqueField(it.typ.elementType, v)
addUniqueField(it.typ[0], v)
result = indirectAccess(newSymNode(it), v, v.info)
else:
result = newSymNode(v)
@@ -619,7 +569,7 @@ proc setupEnvVar(owner: PSym; d: var DetectionPass;
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
c.envVars[owner.id] = result
if optOwnedRefs in d.graph.config.globalOptions:
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), d.idgen, owner, info)
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextSymId d.idgen, owner, info)
v.flags = {sfShadowed, sfGeneratedOp}
v.typ = envVarType
c.unownedEnvVars[owner.id] = newSymNode(v)
@@ -673,7 +623,7 @@ proc rawClosureCreation(owner: PSym;
if owner.kind != skMacro:
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
markInjectDestructors(owner)
owner.flags.incl sfInjectDestructors
let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:
@@ -699,27 +649,16 @@ proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
res.add newAsgnStmt(unowned, nilLit, info)
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
proc getUpForIter(g: ModuleGraph; owner, iterOwner: PSym, expectedUpTyp: PType): PNode =
var p = getHiddenParam(g, owner)
var res = p.newSymNode
while res.typ.skipTypes({tyOwned, tyRef, tyPtr}) != expectedUpTyp:
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
if upField == nil:
return nil
p = upField
res = rawIndirectAccess(res, upField, p.info)
res
proc closureCreationForIter(owner: PSym, iter: PNode;
proc closureCreationForIter(iter: PNode;
d: var DetectionPass; c: var LiftingPass): PNode =
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
let iterOwner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
incl(v.flagsImpl, sfShadowed)
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), nextSymId(d.idgen), owner, iter.info)
incl(v.flags, sfShadowed)
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
var vnode: PNode
if iterOwner.isIterator:
let it = getHiddenParam(d.graph, iterOwner)
if owner.isIterator:
let it = getHiddenParam(d.graph, owner)
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
@@ -728,14 +667,12 @@ proc closureCreationForIter(owner: PSym, iter: PNode;
addVar(vs, vnode)
result.add(vs)
result.add genCreateEnv(vnode)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:
let expectedUpTyp = upField.typ.skipTypes({tyOwned, tyRef, tyPtr})
let u = if iterOwner == owner: setupEnvVar(iterOwner, d, c, iter.info)
else: getUpForIter(d.graph, owner, iterOwner, expectedUpTyp)
if u != nil and u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == expectedUpTyp:
let u = setupEnvVar(owner, d, c, iter.info)
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
@@ -769,12 +706,11 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
let available = getHiddenParam(d.graph, owner)
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(owner, n, d, c)
result = closureCreationForIter(n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
else:
result = nil
let available = getHiddenParam(d.graph, owner)
let wanted = getHiddenParam(d.graph, s).typ
# ugh: call through some other inner proc;
@@ -801,7 +737,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
# echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer
c.inContainer = 0
var body = transformBody(d.graph, d.idgen, s, {})
var body = transformBody(d.graph, d.idgen, s, dontUseCache)
body = liftCapturedVars(body, s, d, c)
if c.envVars.getOrDefault(s.id).isNil:
s.transformedBody = body
@@ -816,6 +752,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
elif s.id in d.capturedVars:
if s.owner != owner:
result = accessViaEnvParam(d.graph, n, owner)
elif owner.isIterator and interestingIterVar(s):
result = accessViaEnvParam(d.graph, n, owner)
else:
result = accessViaEnvVar(n, owner, d, c)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
@@ -854,8 +792,6 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
of nkTypeOfExpr:
result = n
else:
if n.isCallExpr and n[0].isTypeOf:
return
if owner.isIterator:
if nfLL in n.flags:
# special case 'when nimVm' due to bug #3636:
@@ -923,19 +859,20 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
fn.typ.callConv = oldCC
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
idgen: IdGenerator; flags: TransformFlags): PNode =
idgen: IdGenerator): PNode =
# XXX backend == backendJs does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
# However we can do lifting for the stuff which is *only* compiletime.
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and
if body.kind == nkEmpty or (
g.config.backend == backendJs and not isCompileTime) or
(fn.skipGenericOwner.kind != skModule and force notin flags):
fn.skipGenericOwner.kind != skModule:
# ignore forward declaration:
result = body
tooEarly = true
if fn.isIterator:
var d = initDetectionPass(g, fn, idgen)
addClosureParam(d, fn, body.info)
else:
var d = initDetectionPass(g, fn, idgen)
detectCapturedVars(body, fn, d)
@@ -999,14 +936,14 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
result = newNodeI(nkStmtList, body.info)
# static binding?
var env: PSym = nil
var env: PSym
let op = call[0]
if op.kind == nkSym and op.sym.isIterator:
# createClosure()
let iter = op.sym
let hp = getHiddenParam(g, iter)
env = newSym(skLet, iter.name, idgen, owner, body.info)
env = newSym(skLet, iter.name, nextSymId(idgen), owner, body.info)
env.typ = hp.typ
env.flags = hp.flags
@@ -1024,20 +961,6 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
for i in 0..<op.len-1:
result.add op[i]
elif op.kind != nkSym: # might have side effects
# bug #25046
# create a temp for the closure
# var :closureTemp
# :closureTemp = ...
let tempSym = newSym(skLet, getIdent(g.cache, ":closureTemp"), idgen, owner, body.info)
tempSym.typ = call[0].typ
let temp = newSymNode(tempSym)
var v = newNodeI(nkVarSection, body.info)
addVar(v, temp)
result.add(v)
result.add newAsgnStmt(temp, call[0], body.info)
call[0] = temp
var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))
@@ -1048,15 +971,12 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
# gather vars in a tuple:
var v2 = newNodeI(nkLetSection, body.info)
var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
if body.len == 3 and body[0].kind == nkVarTuple:
vpart = body[0] # fixes for (i,j) in walk() # bug #15924
else:
for i in 0..<body.len-2:
if body[i].kind == nkSym:
body[i].sym.transitionToLet()
vpart.add body[i]
for i in 0..<body.len-2:
if body[i].kind == nkSym:
body[i].sym.transitionToLet()
vpart.add body[i]
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
if not env.isNil:
call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
vpart.add call

Some files were not shown because too many files have changed in this diff Show More