mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
322 Commits
v1.6.12
...
version-1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a1f77cb18 | ||
|
|
a57b1d7fac | ||
|
|
f577229a6f | ||
|
|
b057bb6654 | ||
|
|
af2b7ff28f | ||
|
|
f964fdbec0 | ||
|
|
d5a7fb8ea0 | ||
|
|
22e71cbc0f | ||
|
|
f40c941580 | ||
|
|
e5a3f30519 | ||
|
|
286b94e7de | ||
|
|
52155b5587 | ||
|
|
9edb694173 | ||
|
|
8be2614608 | ||
|
|
bd2b298244 | ||
|
|
311b7aaf37 | ||
|
|
a13fd00729 | ||
|
|
a8c706760c | ||
|
|
86298e30d9 | ||
|
|
19440baa80 | ||
|
|
a0d62e654a | ||
|
|
67d9848027 | ||
|
|
4663a261f9 | ||
|
|
69322f86de | ||
|
|
f29e7cfdcd | ||
|
|
bf85f35226 | ||
|
|
f76ed447c2 | ||
|
|
0ca09f64cf | ||
|
|
52ad63bc22 | ||
|
|
98073624f7 | ||
|
|
c32e002e63 | ||
|
|
5b920dc01f | ||
|
|
4234a6373a | ||
|
|
f6ada8874a | ||
|
|
4088d7f2ae | ||
|
|
ad29fe8946 | ||
|
|
52a3d38bda | ||
|
|
1b530b5897 | ||
|
|
9193c85b61 | ||
|
|
758a54ce23 | ||
|
|
cc49902fa2 | ||
|
|
8ec5dc1969 | ||
|
|
dc5e8fc39d | ||
|
|
37c3465081 | ||
|
|
f713c54166 | ||
|
|
dc519a539b | ||
|
|
acfd1b272d | ||
|
|
53fc3924a0 | ||
|
|
95d4961612 | ||
|
|
b5ec602763 | ||
|
|
9a649d91ad | ||
|
|
e6cdbad6a5 | ||
|
|
e79e0616b1 | ||
|
|
6b555e78b7 | ||
|
|
36619e3569 | ||
|
|
f4ae3ab729 | ||
|
|
289e35166b | ||
|
|
338f25b56f | ||
|
|
872795e18a | ||
|
|
58515f6042 | ||
|
|
59a35563cf | ||
|
|
66c21b9e95 | ||
|
|
94a855893e | ||
|
|
46c9eb5904 | ||
|
|
9cc5927a99 | ||
|
|
85fb709020 | ||
|
|
3483fdd43c | ||
|
|
507d7a9daa | ||
|
|
75f427a57b | ||
|
|
b0f71b051f | ||
|
|
b036c57dd3 | ||
|
|
339c08ce5b | ||
|
|
5c266cdf05 | ||
|
|
234f4a27e1 | ||
|
|
c7cf5d4e73 | ||
|
|
561092003d | ||
|
|
5b168d3f9b | ||
|
|
965f7ebf54 | ||
|
|
da7a6f4875 | ||
|
|
ff9d59747d | ||
|
|
6d5761d488 | ||
|
|
931a70b123 | ||
|
|
0c6c11f2aa | ||
|
|
d15cbb229a | ||
|
|
c90bbe3201 | ||
|
|
45b53ee36f | ||
|
|
20e5173cca | ||
|
|
46d785aa40 | ||
|
|
71027936e8 | ||
|
|
f89db4915f | ||
|
|
d4f961b08a | ||
|
|
8cf8879941 | ||
|
|
5a1ed8f4d4 | ||
|
|
fec5d04afc | ||
|
|
3dd84b36fd | ||
|
|
490bf10a6c | ||
|
|
50de7732ed | ||
|
|
df6fbf6521 | ||
|
|
5cae61ce21 | ||
|
|
122ac6775a | ||
|
|
73c83905af | ||
|
|
22d60a53f7 | ||
|
|
265f565be3 | ||
|
|
6673934482 | ||
|
|
8a8fddcc40 | ||
|
|
99e6d328ec | ||
|
|
2dcf32f5c8 | ||
|
|
6f79408576 | ||
|
|
17ae3016c7 | ||
|
|
02d7a181f6 | ||
|
|
349039678a | ||
|
|
b64e109fdc | ||
|
|
26681769e8 | ||
|
|
c160b2161e | ||
|
|
fbf28d1ce7 | ||
|
|
b6924383df | ||
|
|
cfd0ae4adb | ||
|
|
04e70f1f4b | ||
|
|
14e1f82f42 | ||
|
|
2304af294f | ||
|
|
5768dd08cd | ||
|
|
8cdba8ea29 | ||
|
|
3bf9ef537f | ||
|
|
bdb679c487 | ||
|
|
a9fa981000 | ||
|
|
3a47e433be | ||
|
|
6d65e69b10 | ||
|
|
faa4720dcf | ||
|
|
6d3a1fcda6 | ||
|
|
bf24379af5 | ||
|
|
0591cc2f8a | ||
|
|
89b39ee8fe | ||
|
|
d038e1b750 | ||
|
|
a6846b81eb | ||
|
|
de43bdfa70 | ||
|
|
25a4026cda | ||
|
|
2e9d595f92 | ||
|
|
13ad15dffe | ||
|
|
0ff510ca07 | ||
|
|
16eaebbf51 | ||
|
|
61c8a2b1e7 | ||
|
|
b64d59635c | ||
|
|
cd28e01a1a | ||
|
|
2f557f74e5 | ||
|
|
f1ec998e71 | ||
|
|
a3d5a3ca9b | ||
|
|
74350447ed | ||
|
|
83c1ec46ce | ||
|
|
29b6dd6196 | ||
|
|
8994e8f2e2 | ||
|
|
3e76777de8 | ||
|
|
d237b73e6e | ||
|
|
7134d57cd4 | ||
|
|
17ddc7e894 | ||
|
|
26331a9005 | ||
|
|
6d2884be9c | ||
|
|
e636b47429 | ||
|
|
4028514eeb | ||
|
|
8b956dba09 | ||
|
|
78f9933b8f | ||
|
|
46be787fb2 | ||
|
|
7afe6b1f4b | ||
|
|
f9d30dc1e5 | ||
|
|
89b244a2f5 | ||
|
|
4c120c1978 | ||
|
|
bc601a62a2 | ||
|
|
9167eb6c89 | ||
|
|
7c4bc63b00 | ||
|
|
261592de18 | ||
|
|
f52395f2d8 | ||
|
|
d807c4b7c0 | ||
|
|
65aa564c54 | ||
|
|
0f05a0dd5b | ||
|
|
512727d75e | ||
|
|
00408a4554 | ||
|
|
09be379b91 | ||
|
|
6f520fb6d9 | ||
|
|
789a984c1e | ||
|
|
1edb81b66c | ||
|
|
340c9248f1 | ||
|
|
9ec09bfa55 | ||
|
|
a13f623f16 | ||
|
|
5129a1e88d | ||
|
|
3f6a08d5b0 | ||
|
|
299f1e99cd | ||
|
|
a02a4d8fc5 | ||
|
|
10935a71ed | ||
|
|
d0e8ad17b5 | ||
|
|
60100a9a64 | ||
|
|
c8998c498f | ||
|
|
97e0ce3c75 | ||
|
|
aa7b0c8581 | ||
|
|
d6f52f3cef | ||
|
|
fa15083cc9 | ||
|
|
c868f1ef67 | ||
|
|
8f0b891e68 | ||
|
|
4084ef19a3 | ||
|
|
7d444ff04c | ||
|
|
aaf06dba1b | ||
|
|
0e1dd54fee | ||
|
|
c78cee697d | ||
|
|
4c4116d63a | ||
|
|
9d17d02734 | ||
|
|
e8578c926e | ||
|
|
5937c40928 | ||
|
|
f3e088f11a | ||
|
|
39122ecd93 | ||
|
|
7ea60f78b5 | ||
|
|
ee260dd838 | ||
|
|
23881b188e | ||
|
|
e5f968da0c | ||
|
|
cb2f8d4d39 | ||
|
|
e2aa1d6d84 | ||
|
|
beaead0fd1 | ||
|
|
763486a3aa | ||
|
|
d61dd518d9 | ||
|
|
2a292c0e71 | ||
|
|
814e237ab6 | ||
|
|
8324b7a86d | ||
|
|
7ccefbc422 | ||
|
|
8fddc8e8a1 | ||
|
|
f47d977f6d | ||
|
|
a5c98bf3af | ||
|
|
93b868bff9 | ||
|
|
274f94c84a | ||
|
|
1ff3494dab | ||
|
|
1d43c8620e | ||
|
|
f5e096b13f | ||
|
|
29c099ccd1 | ||
|
|
6887d6de80 | ||
|
|
7c2d64c18d | ||
|
|
f991d2c1fd | ||
|
|
d6ae4e00f4 | ||
|
|
2a2a9110b2 | ||
|
|
4a7ccfa333 | ||
|
|
476d677c6f | ||
|
|
ee0c1482d7 | ||
|
|
731d1dff84 | ||
|
|
e02dc971f5 | ||
|
|
5f77d5ad85 | ||
|
|
f69dac8038 | ||
|
|
338f43f305 | ||
|
|
d1d8cd1000 | ||
|
|
f042acc535 | ||
|
|
eac5452082 | ||
|
|
80861998c7 | ||
|
|
c1fd463834 | ||
|
|
dedff71ca0 | ||
|
|
cd60067638 | ||
|
|
bf047d6ef1 | ||
|
|
313b2f0d71 | ||
|
|
f5269876ea | ||
|
|
89738fd95d | ||
|
|
ab51a8e08b | ||
|
|
6ffe5ee7e8 | ||
|
|
ab2a8344b3 | ||
|
|
fc9cccccd3 | ||
|
|
faf5ffd970 | ||
|
|
6fc9327ebf | ||
|
|
93aeb9c825 | ||
|
|
59e8ca721e | ||
|
|
193b3c66bb | ||
|
|
0b134412f8 | ||
|
|
1526ae8791 | ||
|
|
b914573d18 | ||
|
|
f579d29b68 | ||
|
|
f79f7fed98 | ||
|
|
e0c4015bbe | ||
|
|
7b500cbfad | ||
|
|
53e4692665 | ||
|
|
b0e595b3cf | ||
|
|
4e3c997d40 | ||
|
|
f16bb4d85b | ||
|
|
8f1dea614e | ||
|
|
63d710f5a1 | ||
|
|
2ebd47089a | ||
|
|
535f0b9c9d | ||
|
|
813b371758 | ||
|
|
a09379f5fd | ||
|
|
e9f7455bd6 | ||
|
|
e03b1b79ae | ||
|
|
0ea770c8f0 | ||
|
|
8a0012de42 | ||
|
|
1d4916b481 | ||
|
|
2de758e68a | ||
|
|
c8c9699850 | ||
|
|
5df7f3fd5c | ||
|
|
44d45b763c | ||
|
|
f7c150766a | ||
|
|
4ae143ca60 | ||
|
|
0a8dcd5e11 | ||
|
|
929aa99665 | ||
|
|
bae10d44fa | ||
|
|
4632e8b26e | ||
|
|
97e2e06b00 | ||
|
|
b827431499 | ||
|
|
99d448deeb | ||
|
|
9afd19b236 | ||
|
|
0d0cf86401 | ||
|
|
5785444520 | ||
|
|
1b3f5eed70 | ||
|
|
517538dcf8 | ||
|
|
59916007cc | ||
|
|
a5bcbe6dff | ||
|
|
cd3d4b5e05 | ||
|
|
ac2181c5b9 | ||
|
|
4675aae90b | ||
|
|
a0402b2012 | ||
|
|
cbdc48ec66 | ||
|
|
f19edae1b8 | ||
|
|
c5b9a44894 | ||
|
|
3009eafc89 | ||
|
|
1e97abec96 | ||
|
|
95a655a975 | ||
|
|
39f5a2b218 | ||
|
|
6c1f389d72 | ||
|
|
1ae9cac3a4 | ||
|
|
fa7f5742d3 | ||
|
|
c8314b7c9c | ||
|
|
52ea15cc24 | ||
|
|
7926440ae3 | ||
|
|
bda6e071cd |
@@ -1,33 +0,0 @@
|
||||
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
|
||||
# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- databases/sqlite3
|
||||
- devel/boehm-gc-threaded
|
||||
- devel/pcre
|
||||
- devel/sdl20
|
||||
- devel/sfml
|
||||
- www/node
|
||||
- devel/gmake
|
||||
|
||||
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "0_1"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
@@ -1,33 +0,0 @@
|
||||
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
|
||||
image: openbsd/latest
|
||||
packages:
|
||||
- gmake
|
||||
- sqlite3
|
||||
- node
|
||||
- boehm-gc
|
||||
- pcre
|
||||
- sfml
|
||||
- sdl2
|
||||
- libffi
|
||||
|
||||
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "0_2"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
@@ -1,33 +0,0 @@
|
||||
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
|
||||
image: openbsd/latest
|
||||
packages:
|
||||
- gmake
|
||||
- sqlite3
|
||||
- node
|
||||
- boehm-gc
|
||||
- pcre
|
||||
- sfml
|
||||
- sdl2
|
||||
- libffi
|
||||
|
||||
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "1_2"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -4,6 +4,3 @@
|
||||
# duplicated, which is easily identifiable and fixable.
|
||||
/changelog.md merge=union
|
||||
|
||||
# bug https://github.com/dom96/choosenim/issues/256 for WSL CRLF
|
||||
*.sh text eol=lf
|
||||
/config/build_config.txt text eol=lf
|
||||
|
||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,7 +1,7 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: 'araq' # Replace with a single Patreon username
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: 'nim' # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
|
||||
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
39
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,53 +1,46 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Have you found an unexpected behavior? Use this template.
|
||||
title: Think about the title, twice
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
(Consider writing a PR targetting devel branch after filing this, see [contributing.html](https://nim-lang.github.io/Nim/contributing.html)).
|
||||
<!-- Think about the title, twice. -->
|
||||
|
||||
<!-- Summarize the problem here, keep it short and simple. -->
|
||||
Function `echo` outputs the wrong string.
|
||||
|
||||
|
||||
### Example
|
||||
<!-- Paste your example in the code-block below. -->
|
||||
```nim
|
||||
echo "Hello World!"
|
||||
# This code should be a minimum reproducible example:
|
||||
# try to simplify and minimize as much as possible. If it's a compiler
|
||||
# issue, try to minimize further by removing any imports if possible.
|
||||
```
|
||||
|
||||
|
||||
### Current Output
|
||||
please check whether the problem still exists in git head before posting,
|
||||
see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
||||
```
|
||||
Hola mundo!
|
||||
```
|
||||
|
||||
|
||||
### Expected Output
|
||||
<!-- What should be the correct output? -->
|
||||
```
|
||||
Hello World!
|
||||
```
|
||||
|
||||
### Possible Solution
|
||||
|
||||
* In file xyz there is a call that might be the cause of it.
|
||||
### Possible Solution
|
||||
<!--- Have you found a possible solution? Post it here. -->
|
||||
|
||||
|
||||
### Additional Information
|
||||
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`).
|
||||
|
||||
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.
|
||||
|
||||
* Issue #abc is related, but different because of ...
|
||||
* This issue is blocking my project xyz
|
||||
|
||||
<!--- For Example:
|
||||
* Your Nim version (output of `nim -v`).
|
||||
* Was it working in the previous Nim releases?
|
||||
* A link to a related issue or discussion.
|
||||
* A project reference where (and how) the issue causes problems.
|
||||
-->
|
||||
```
|
||||
$ nim -v
|
||||
Nim Compiler Version 0.1.2
|
||||
# make sure to include the git hash if not using a tagged release
|
||||
```
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
10
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,10 +0,0 @@
|
||||
contact_links:
|
||||
- name: Forum
|
||||
url: https://forum.nim-lang.org
|
||||
about: Please ask and answer questions here.
|
||||
- name: Discord
|
||||
url: https://discord.gg/nim
|
||||
about: Please ask and answer questions here.
|
||||
- name: Stack Overflow
|
||||
url: https://stackoverflow.com/questions/tagged/nim-lang
|
||||
about: Please ask and answer questions here.
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,9 +1,6 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Do you want to suggest a new feature? Use this template.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
69
.github/stale.yml
vendored
69
.github/stale.yml
vendored
@@ -1,69 +0,0 @@
|
||||
# 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
|
||||
111
.github/workflows/ci_docs.yml
vendored
111
.github/workflows/ci_docs.yml
vendored
@@ -1,111 +0,0 @@
|
||||
name: Nim Docs CI
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'compiler/docgen.nim'
|
||||
- 'compiler/renderverbatim.nim'
|
||||
- 'config/nimdoc.cfg'
|
||||
- 'doc/**.rst'
|
||||
- 'doc/nimdoc.css'
|
||||
- 'lib/**.nim'
|
||||
- 'nimdoc/testproject/expected/testproject.html'
|
||||
- 'tools/dochack/dochack.nim'
|
||||
- 'tools/kochdocs.nim'
|
||||
- '.github/workflows/ci_docs.yml'
|
||||
- 'koch.nim'
|
||||
pull_request:
|
||||
# Run only on changes on these files.
|
||||
paths:
|
||||
- 'compiler/docgen.nim'
|
||||
- 'compiler/renderverbatim.nim'
|
||||
- 'config/nimdoc.cfg'
|
||||
- 'doc/**.rst'
|
||||
- 'doc/nimdoc.css'
|
||||
- 'lib/**.nim'
|
||||
- 'nimdoc/testproject/expected/testproject.html'
|
||||
- 'tools/dochack/dochack.nim'
|
||||
- 'tools/kochdocs.nim'
|
||||
- '.github/workflows/ci_docs.yml'
|
||||
- 'koch.nim'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-20.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-11
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install build dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install make
|
||||
|
||||
- name: 'Install build dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'System information'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimCiSystemInfo
|
||||
|
||||
- name: 'Build csourcesAny (posix)'
|
||||
# this would work on windows and other CI use this on windows,
|
||||
# but we ensure here that `ci/build_autogen.bat` keeps working on windows.
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc
|
||||
# was previously using caching via `actions/cache@v1` but this wasn't
|
||||
# used in other CI pipelines and it's unclear the added complexity
|
||||
# was worth the saving; can be revisited if needed.
|
||||
|
||||
- name: 'Build csourcesAny (windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: cmd
|
||||
run: ci/build_autogen.bat
|
||||
|
||||
- name: 'Build koch'
|
||||
shell: bash
|
||||
run: nim c koch
|
||||
|
||||
- name: 'Build the real compiler'
|
||||
shell: bash
|
||||
run: ./koch boot -d:release
|
||||
|
||||
- name: 'Build documentation'
|
||||
shell: bash
|
||||
run: ./koch doc --git.commit:devel
|
||||
|
||||
- name: 'Publish documentation to Github Pages'
|
||||
if: |
|
||||
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
||||
matrix.target == 'linux'
|
||||
uses: crazy-max/ghaction-github-pages@v1
|
||||
with:
|
||||
build_dir: doc/html
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
63
.github/workflows/ci_packages.yml
vendored
63
.github/workflows/ci_packages.yml
vendored
@@ -1,63 +0,0 @@
|
||||
name: Packages CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
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
|
||||
env:
|
||||
NIM_TEST_PACKAGES: "1"
|
||||
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
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
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'System information'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimCiSystemInfo
|
||||
|
||||
- name: 'Build csourcesAny'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'koch, Run CI'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
33
.gitignore
vendored
33
.gitignore
vendored
@@ -30,7 +30,7 @@ deinstall.sh
|
||||
|
||||
doc/html/
|
||||
doc/*.html
|
||||
doc/pdf
|
||||
doc/*.pdf
|
||||
doc/*.idx
|
||||
/web/upload
|
||||
/build/*
|
||||
@@ -64,48 +64,23 @@ lib/**/*.html
|
||||
testament.db
|
||||
/tests/**/*.json
|
||||
/tests/**/*.js
|
||||
|
||||
/csources
|
||||
/csources_v1
|
||||
|
||||
/dist/
|
||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
||||
dist/
|
||||
|
||||
# Private directories and files (IDEs)
|
||||
.*/
|
||||
~*
|
||||
|
||||
# testament cruft; TODO: generate these in a gitignore'd dir (./build) in the first place.
|
||||
# testament cruft; TODO: generate these in a gitignore'd dir in the first place.
|
||||
testresults/
|
||||
test.txt
|
||||
/test.ini
|
||||
|
||||
tweeter.db
|
||||
tweeter_test.db
|
||||
|
||||
/tests/megatest.nim
|
||||
/tests/ic/*_temp.nim
|
||||
/tests/navigator/*_temp.nim
|
||||
|
||||
megatest.nim
|
||||
|
||||
/outputExpected.txt
|
||||
/outputGotten.txt
|
||||
/t15148.txt
|
||||
/tests/vm/tfile_rw.txt
|
||||
|
||||
/lib/pure/*.js
|
||||
|
||||
!/.builds/
|
||||
|
||||
!/.github
|
||||
|
||||
# ignore debug dirs generated by dsymutil on OSX
|
||||
*.dSYM
|
||||
|
||||
# for `nim c -r nimdoc/tester` etc; this can be in multiple places
|
||||
htmldocs
|
||||
|
||||
## these are not needed anymore unless checkout old older versions
|
||||
nimdoc.out.css
|
||||
# except here:
|
||||
!/nimdoc/testproject/expected/*
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# xxx unused, out of date
|
||||
|
||||
image: ubuntu:18.04
|
||||
image: ubuntu:16.04
|
||||
|
||||
stages:
|
||||
- pre-build
|
||||
@@ -56,7 +54,7 @@ test-windows:
|
||||
script:
|
||||
- call ci\deps.bat
|
||||
- nim c testament\tester
|
||||
- testament\tester.exe all
|
||||
- testament\tester.exe --pedantic all
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
|
||||
94
.travis.yml
Normal file
94
.travis.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
sudo: false
|
||||
language: c
|
||||
|
||||
dist: xenial
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
- os: linux
|
||||
env:
|
||||
- NIM_COMPILE_TO_CPP=false
|
||||
- CPU=amd64
|
||||
|
||||
- os: linux
|
||||
env:
|
||||
- NIM_COMPILE_TO_CPP=false
|
||||
- CPU=i386
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-multilib
|
||||
- g++-multilib
|
||||
- libcurl4-openssl-dev:i386
|
||||
- libgc-dev:i386
|
||||
- libglib2.0-dev:i386
|
||||
- libpulse-dev:i386
|
||||
- libsdl1.2-dev:i386
|
||||
- libsfml-dev:i386
|
||||
|
||||
- os: osx
|
||||
env:
|
||||
- NIM_COMPILE_TO_CPP=false
|
||||
- CPU=amd64
|
||||
|
||||
- os: osx
|
||||
env:
|
||||
- NIM_COMPILE_TO_CPP=true
|
||||
- CPU=amd64
|
||||
|
||||
# To allow failures for a failing configuration, use something like:
|
||||
# allow_failures:
|
||||
# - env: NIM_COMPILE_TO_CPP=true
|
||||
# - os: osx
|
||||
|
||||
addons:
|
||||
apt:
|
||||
# update the list above if more deps are introduced
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libsdl1.2-dev
|
||||
- libgc-dev
|
||||
- libsfml-dev
|
||||
- libc6-dbg
|
||||
- valgrind
|
||||
homebrew:
|
||||
packages:
|
||||
- boehmgc
|
||||
- make
|
||||
- sfml
|
||||
update: true
|
||||
|
||||
install:
|
||||
- if [[ $CPU = i386 ]]; then echo -e "#!/bin/bash\n$(which gcc) -m32 \"\$@\"" > bin/gcc; fi
|
||||
- if [[ $CPU = i386 ]]; then chmod 755 bin/gcc; fi
|
||||
- if [[ $CPU = i386 ]]; then echo -e "#!/bin/bash\n$(which g++) -m32 \"\$@\"" > bin/g++; fi
|
||||
- if [[ $CPU = i386 ]]; then chmod 755 bin/g++; fi
|
||||
|
||||
before_script:
|
||||
- git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
- export PATH="$PWD/bin${PATH:+:$PATH}"
|
||||
- make -C csources -j 2 LD=$CC ucpu=$CPU
|
||||
|
||||
script:
|
||||
- echo "travis_fold:start:nim_c_koch"
|
||||
- nim c koch
|
||||
- echo "travis_fold:end:nim_c_koch"
|
||||
- ./koch runCI
|
||||
|
||||
before_deploy:
|
||||
# Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html
|
||||
- cp -f ./doc/html/overview.html ./doc/html/index.html
|
||||
deploy: # https://nim-lang.github.io/Nim
|
||||
provider: pages
|
||||
# local-dir *has* to be a relative path from the repo root.
|
||||
local-dir: "doc/html"
|
||||
skip-cleanup: true
|
||||
github-token: "$GITHUB_OAUTH_TOKEN"
|
||||
keep-history: false
|
||||
on:
|
||||
branch: devel
|
||||
|
||||
# Extract failed tests
|
||||
after_failure: nim c -r tools/ci_testresults.nim
|
||||
35
appveyor.yml
Normal file
35
appveyor.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
version: '{build}'
|
||||
|
||||
environment:
|
||||
DLLS_URL: https://nim-lang.org/download/dlls.zip
|
||||
DLLS_ARCHIVE: dlls.zip
|
||||
MINGW_DIR: mingw64
|
||||
MINGW_URL: https://nim-lang.org/download/mingw64-6.3.0.7z
|
||||
MINGW_ARCHIVE: mingw64-6.3.0.7z
|
||||
|
||||
matrix:
|
||||
- NIM_TEST_PACKAGES: false
|
||||
- NIM_TEST_PACKAGES: true
|
||||
|
||||
cache:
|
||||
- '%MINGW_ARCHIVE%'
|
||||
- '%DLLS_ARCHIVE%'
|
||||
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 8 # node 8 or later is required to test js async stuff
|
||||
- IF not exist "%DLLS_ARCHIVE%" appveyor DownloadFile "%DLLS_URL%" -FileName "%DLLS_ARCHIVE%"
|
||||
- 7z x -y "%DLLS_ARCHIVE%" -o"%CD%\BIN"> nul
|
||||
- IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%"
|
||||
- 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul
|
||||
- SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH%
|
||||
- git clone --depth 1 https://github.com/nim-lang/csources
|
||||
- cd csources
|
||||
- build64.bat
|
||||
- cd ..
|
||||
|
||||
build_script:
|
||||
- bin\nim c koch
|
||||
- koch runCI
|
||||
|
||||
deploy: off
|
||||
@@ -7,161 +7,162 @@ pr:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
variables:
|
||||
- name: skipci
|
||||
value: false
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
CPU: amd64
|
||||
Linux_i386:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-10.14'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-10.14'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64:
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
Windows_amd64_pkg:
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TEST_PACKAGES: true
|
||||
|
||||
jobs:
|
||||
- job: packages
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
timeoutInMinutes: 90 # default `60` led to lots of cancelled jobs; use 0 for unlimited (may be undesirable)
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-18.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_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-2019'
|
||||
CPU: amd64
|
||||
# see also: `NIM_TEST_PACKAGES`
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
Windows_amd64_batch1_3:
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
Windows_amd64_batch2_3:
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "2_3"
|
||||
steps:
|
||||
- bash: git config --global core.autocrlf false
|
||||
displayName: 'Disable auto conversion to CRLF by git (Windows-only)'
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
workspace:
|
||||
clean: all
|
||||
- bash: git clone --depth 1 https://github.com/nim-lang/csources
|
||||
displayName: 'Checkout Nim csources'
|
||||
|
||||
steps:
|
||||
- bash: git config --global core.autocrlf false
|
||||
displayName: 'Disable auto conversion to CRLF by git (Windows-only)'
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '8.x'
|
||||
displayName: 'Install node.js 8.x'
|
||||
|
||||
- checkout: self
|
||||
fetchDepth: 2 # see D20210329T004830
|
||||
- bash: |
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
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(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
if nimIsCiSkip; then
|
||||
echo '##vso[task.setvariable variable=skipci]true'
|
||||
fi
|
||||
displayName: 'Check whether to skip CI'
|
||||
- bash: |
|
||||
sudo dpkg --add-architecture i386
|
||||
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '16.x'
|
||||
displayName: 'Install node.js 16.x'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
# Downgrade llvm:
|
||||
# - llvm has to be downgraded to have 32bit version installed for sfml.
|
||||
cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
|
||||
Package: libllvm6.0
|
||||
Pin: origin "azure.archive.ubuntu.com"
|
||||
Pin-Priority: 1001
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
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'))
|
||||
EOF
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo dpkg --add-architecture i386
|
||||
# Downgrade llvm:
|
||||
# - llvm has to be downgraded to have 32bit version installed for sfml.
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
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
|
||||
|
||||
cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
|
||||
Package: libllvm6.0
|
||||
Pin: origin "azure.archive.ubuntu.com"
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
|
||||
# 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-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
|
||||
exec $(which gcc) -m32 "\$@"
|
||||
EOF
|
||||
cat << EOF > bin/g++
|
||||
#!/bin/bash
|
||||
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
exec $(which g++) -m32 "\$@"
|
||||
EOF
|
||||
|
||||
exec $(which gcc) -m32 "\$@"
|
||||
EOF
|
||||
chmod 755 bin/gcc
|
||||
chmod 755 bin/g++
|
||||
|
||||
cat << EOF > bin/g++
|
||||
#!/bin/bash
|
||||
displayName: 'Install dependencies (i386 Linux)'
|
||||
condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386'))
|
||||
|
||||
exec $(which g++) -m32 "\$@"
|
||||
EOF
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: eq(variables['Agent.OS'], 'Darwin')
|
||||
|
||||
echo_run chmod 755 bin/gcc
|
||||
echo_run chmod 755 bin/g++
|
||||
- bash: |
|
||||
mkdir dist
|
||||
curl -L https://nim-lang.org/download/mingw64-6.3.0.7z -o dist/mingw64.7z
|
||||
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
7z x dist/mingw64.7z -odist
|
||||
7z x dist/dlls.zip -obin
|
||||
echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
||||
|
||||
displayName: 'Install dependencies (i386 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386'))
|
||||
displayName: 'Install dependencies (Windows)'
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
||||
- bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin'
|
||||
displayName: 'Add build binaries to PATH'
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
||||
- bash: |
|
||||
echo 'PATH:' "$PATH"
|
||||
echo '##[section]gcc version'
|
||||
gcc -v
|
||||
echo '##[section]nodejs version'
|
||||
node -v
|
||||
echo '##[section]make version'
|
||||
make -v
|
||||
displayName: 'System information'
|
||||
|
||||
displayName: 'Install dependencies (Windows)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
- bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)"
|
||||
displayName: 'Get csources version'
|
||||
|
||||
- bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
displayName: 'Add build binaries to PATH'
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'csources | "$(Agent.OS)" | $(CPU) | $(csources_version)'
|
||||
path: csources/bin
|
||||
displayName: 'Restore built csources'
|
||||
|
||||
- bash: . ci/funs.sh && nimCiSystemInfo
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
displayName: 'System information'
|
||||
|
||||
- bash: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu=$(CPU)
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
displayName: 'Build csourcesAny'
|
||||
- bash: |
|
||||
ncpu=
|
||||
ext=
|
||||
case '$(Agent.OS)' in
|
||||
'Linux')
|
||||
ncpu=$(nproc)
|
||||
;;
|
||||
'Darwin')
|
||||
ncpu=$(sysctl -n hw.ncpu)
|
||||
;;
|
||||
'Windows_NT')
|
||||
ncpu=$NUMBER_OF_PROCESSORS
|
||||
ext=.exe
|
||||
;;
|
||||
esac
|
||||
[[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1
|
||||
|
||||
# this could be revived if performance justifies it (needs a few updates)
|
||||
# - task: Cache@2
|
||||
# inputs:
|
||||
# key: 'csourcesAny | "$(Agent.OS)" | $(CPU) | $(csources_version)'
|
||||
# path: $(nim_csources)
|
||||
# condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
# displayName: 'Restore built csourcesAny'
|
||||
if [[ -x csources/bin/nim$ext ]]; then
|
||||
echo "Found cached compiler, skipping build"
|
||||
else
|
||||
make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no
|
||||
fi
|
||||
cp csources/bin/nim$ext bin
|
||||
displayName: 'Build 1-stage compiler from csources'
|
||||
|
||||
- bash: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
# would could also show on failure: echo '##vso[task.complete result=Failed]'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
displayName: 'koch, Run CI'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
- bash: nim c koch
|
||||
displayName: 'Build koch'
|
||||
|
||||
# set result to omit the "bash exited with error code '1'" message
|
||||
- bash: ./koch runCI || echo '##vso[task.complete result=Failed]'
|
||||
displayName: 'Run CI'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
|
||||
12
bin/nim-gdb
12
bin/nim-gdb
@@ -5,15 +5,9 @@ set -e
|
||||
|
||||
which nim > /dev/null || (echo "nim not in PATH"; exit 1)
|
||||
which gdb > /dev/null || (echo "gdb not in PATH"; exit 1)
|
||||
which readlink > /dev/null || (echo "readlink not in PATH."; exit 1)
|
||||
|
||||
if [[ "$(uname -s)" == "Darwin" || "(uname -s)" == *"BSD" ]]; then
|
||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim))))
|
||||
else
|
||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
|
||||
fi
|
||||
|
||||
# Find out where the pretty printer Python module is
|
||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
|
||||
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py"
|
||||
|
||||
# Run GDB with the additional arguments that load the pretty printers
|
||||
@@ -22,4 +16,6 @@ GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py"
|
||||
NIM_GDB="${NIM_GDB:-gdb}"
|
||||
# exec replaces the new process of bash with gdb. It is always good to
|
||||
# have fewer processes.
|
||||
exec "${NIM_GDB}" -eval-command="source $GDB_PYTHON_MODULE_PATH" "$@"
|
||||
exec ${NIM_GDB} \
|
||||
-eval-command "source $GDB_PYTHON_MODULE_PATH" \
|
||||
"$@"
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
@echo off
|
||||
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\nim-gdb.py
|
||||
set @NIM_GDB=gdb.exe
|
||||
|
||||
@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
|
||||
%@NIM_GDB% --command="wingdbcommand.txt" %*
|
||||
del wingdbcommand.txt /f /q
|
||||
|
||||
EXIT /B %ERRORLEVEL%
|
||||
@echo on
|
||||
@@ -1,29 +1,16 @@
|
||||
@echo off
|
||||
rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
rem Build development version of the compiler; can be rerun safely
|
||||
rem bare bones version of ci/funs.sh adapted for windows.
|
||||
|
||||
rem Read in some common shared variables (shared with other tools),
|
||||
rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
|
||||
for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
|
||||
SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
|
||||
echo "building from csources: %nim_csources%"
|
||||
|
||||
if not exist %nim_csourcesDir% (
|
||||
git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
|
||||
rem build development version of the compiler; can be rerun safely
|
||||
if not exist csources (
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
)
|
||||
|
||||
if not exist %nim_csources% (
|
||||
cd %nim_csourcesDir%
|
||||
git checkout %nim_csourcesHash%
|
||||
echo "%PROCESSOR_ARCHITECTURE%"
|
||||
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
|
||||
if not exist bin\nim.exe (
|
||||
cd csources
|
||||
if PROCESSOR_ARCHITECTURE == AMD64 (
|
||||
SET ARCH=64
|
||||
)
|
||||
CALL build.bat
|
||||
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 --skipUserCfg --skipParentCfg koch
|
||||
koch.exe boot -d:release
|
||||
koch.exe tools
|
||||
36
build_all.sh
Executable file → Normal file
36
build_all.sh
Executable file → Normal file
@@ -1,17 +1,33 @@
|
||||
#!/bin/sh
|
||||
# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
#! /bin/sh
|
||||
|
||||
# build development version of the compiler; can be rerun safely.
|
||||
# arguments can be passed, e.g.:
|
||||
# CC=gcc ucpu=amd64 uos=darwin
|
||||
# build development version of the compiler; can be rerun safely
|
||||
|
||||
set -u # error on undefined variables
|
||||
set -e # exit on first error
|
||||
|
||||
. ci/funs.sh
|
||||
nimBuildCsourcesIfNeeded "$@"
|
||||
echo_run(){
|
||||
printf "\n$*\n"
|
||||
"$@"
|
||||
}
|
||||
|
||||
echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
[ -d csources ] || echo_run git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
|
||||
nim_csources=bin/nim_csources
|
||||
build_nim_csources(){
|
||||
## avoid changing dir in case of failure
|
||||
(
|
||||
echo_run cd csources
|
||||
echo_run sh build.sh $@
|
||||
)
|
||||
# keep $nim_csources in case needed to investigate bootstrap issues
|
||||
# without having to rebuild from csources
|
||||
echo_run cp bin/nim $nim_csources
|
||||
}
|
||||
|
||||
[ -f $nim_csources ] || echo_run build_nim_csources $@
|
||||
|
||||
# Note: if fails, may need to `cd csources && git pull`
|
||||
echo_run bin/nim c --skipUserCfg --skipParentCfg koch
|
||||
|
||||
echo_run ./koch boot -d:release
|
||||
echo_run ./koch tools # Compile Nimble and other tools.
|
||||
|
||||
107
changelog.md
107
changelog.md
@@ -1,116 +1,51 @@
|
||||
# v1.8.x - yyyy-mm-dd
|
||||
# v1.0.8 - xxxx-xx-xx
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- `addr` is now available for all addressable locations,
|
||||
`unsafeAddr` is now deprecated and an alias for `addr`.
|
||||
|
||||
- `io`, `assertions`, `formatfloat`, and `` dollars.`$` `` for objects are about to move out of the `system` module. You may instead import `std/syncio`, `std/assertions`, `std/formatfloat` and `std/objectdollar`.
|
||||
The `-d:nimPreviewSlimSystem` option makes these imports required.
|
||||
|
||||
- The `gc:v2` option is removed.
|
||||
### Breaking changes in the standard library
|
||||
|
||||
- The `mainmodule` and `m` options are removed.
|
||||
- `CountTable.mget` has been removed from `tables.nim`. It didn't work, and it
|
||||
was an oversight to be included in v1.0.
|
||||
|
||||
- The `threads:on` option is now the default.
|
||||
|
||||
- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via
|
||||
`experimental:flexibleOptionalParams`.
|
||||
|
||||
## Standard library additions and changes
|
||||
### Breaking changes in the compiler
|
||||
|
||||
- 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.
|
||||
## Library additions
|
||||
- Fixed buffer overflow bugs in `net`
|
||||
|
||||
- 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.
|
||||
- Added `sections` iterator in `parsecfg`.
|
||||
|
||||
- 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.
|
||||
- Added `browsers.osOpen` const alias for the operating system specific *"open"* command.
|
||||
|
||||
- Several Standard libraries are 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`
|
||||
|
||||
- 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.
|
||||
## Library changes
|
||||
|
||||
- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
- `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.
|
||||
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
|
||||
## Language additions
|
||||
|
||||
|
||||
|
||||
- Fix a bug where calling `close` on io streams in osproc.startProcess was a noop and led to
|
||||
hangs if a process had both reads from stdin and writes (eg to stdout).
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
- 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
|
||||
```
|
||||
- [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental,
|
||||
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `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` now only applies to the current package.
|
||||
### Tool changes
|
||||
|
||||
|
||||
## Tool 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".)
|
||||
### Compiler changes
|
||||
|
||||
- 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`.
|
||||
|
||||
- There is a new switch `--nimMainPrefix:prefix` to influence the `NimMain` that the
|
||||
compiler produces. This is particularly useful for generating static libraries.
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# v1.xx.x - yyyy-mm-dd
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
### 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
|
||||
@@ -15,7 +15,7 @@
|
||||
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
|
||||
inheritance to prevent this issue.
|
||||
- The ``not nil`` type annotation now has to be enabled explicitly
|
||||
via ``{.experimental: "notnil".}`` as we are still not pleased with how this
|
||||
via ``{.experimental: "notnil"}`` as we are still not pleased with how this
|
||||
feature works with Nim's containers.
|
||||
- The parser now warns about inconsistent spacing around binary operators as
|
||||
these can easily be confused with unary operators. This warning will likely
|
||||
|
||||
@@ -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
|
||||
|
||||
75
changelogs/changelog_1_0_4.md
Normal file
75
changelogs/changelog_1_0_4.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# v1.0.4 - 2019-11-26
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim
|
||||
versions. The documentation was improved to acknowledge this special case.
|
||||
See https://github.com/nim-lang/RFCs/issues/175 for more details. (#12688)
|
||||
|
||||
- Add `or detectOs(Manjaro)` (#12587)
|
||||
|
||||
- --os:ios needs to imply defined(macosx) (#12585)
|
||||
|
||||
- export nim.cfg parser (#12602)
|
||||
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed "gorge(Ex) fails after a few calls on OSX"
|
||||
([#12337](https://github.com/nim-lang/Nim/issues/12337))
|
||||
- Fixed "Improve posix module"
|
||||
([#10723](https://github.com/nim-lang/Nim/issues/10723))
|
||||
- Fixed "nim check allows gorgeEx but doesn't allow writeFile"
|
||||
([#12491](https://github.com/nim-lang/Nim/issues/12491))
|
||||
- Fixed "Low level arithmetics bug: -3 mod 7 == 3"
|
||||
([#12514](https://github.com/nim-lang/Nim/issues/12514))
|
||||
- Fixed "Broken link to c2nim in backends.rst/.html"
|
||||
([#12537](https://github.com/nim-lang/Nim/issues/12537))
|
||||
- Fixed "generic argument with default value causes incorrect generic type resolution"
|
||||
([#12528](https://github.com/nim-lang/Nim/issues/12528))
|
||||
- Fixed "regression: compiler/vmgen.nim(354, 20) `false` leaking temporary 10 slotTempInt [AssertionError]
|
||||
([#12547](https://github.com/nim-lang/Nim/issues/12547))
|
||||
- Fixed "Compile/link broken for nim (64-bit only) on Windows"
|
||||
([#12536](https://github.com/nim-lang/Nim/issues/12536))
|
||||
- Fixed "No `=destroy` for elements of closure environments other than for latest devel --gc:destructors"
|
||||
([#12577](https://github.com/nim-lang/Nim/issues/12577))
|
||||
- Fixed "[1.0.0] Cannot compile anything with --cpu:avr"
|
||||
([#12395](https://github.com/nim-lang/Nim/issues/12395))
|
||||
- Fixed "Compiler crash with invalid object variant"
|
||||
([#12379](https://github.com/nim-lang/Nim/issues/12379))
|
||||
- Fixed "push pragma is silently ignored if position is in front of import statement"
|
||||
([#5050](https://github.com/nim-lang/Nim/issues/5050))
|
||||
- Fixed "Error with strformat + asyncdispatch + const"
|
||||
([#12612](https://github.com/nim-lang/Nim/issues/12612))
|
||||
- Fixed "The --nimblePath is additive; need a pain-free workaround"
|
||||
([#12601](https://github.com/nim-lang/Nim/issues/12601))
|
||||
- Fixed "nim.cfg syntax for --define:FOO:VAL undocumented or absent"
|
||||
([#12367](https://github.com/nim-lang/Nim/issues/12367))
|
||||
- Fixed "vm string literals do not work when generated by a macro"
|
||||
([#12670](https://github.com/nim-lang/Nim/issues/12670))
|
||||
- Fixed "StaticRead does force a recompile if static file changes."
|
||||
([#12663](https://github.com/nim-lang/Nim/issues/12663))
|
||||
- Fixed crash in terminate handler
|
||||
([#12572](https://github.com/nim-lang/Nim/pull/12572))
|
||||
- Fixed newLit for objects having string fields
|
||||
([#12542](https://github.com/nim-lang/Nim/pull/12542))
|
||||
|
||||
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
- Documentation Math module (#12460)
|
||||
- Fix many broken links and prefer relative links within docs (#12463)
|
||||
- sequtils: replace deprecated 'random' call within example (#12515)
|
||||
- integer literal documentation (#12513)
|
||||
- Fix code style errors (#12545)
|
||||
- fix several typos in documentation and comments (#12553)
|
||||
- Add docs to better distinguish among getProjectPath, getCurrentDir and currentSourcePath (#12565)
|
||||
- doc/tut3.rst: Fix typo in Introduction (#12607)
|
||||
- Add links to packaging and distro pages (#12603)
|
||||
- fix documentation of `$`\*(dt: DateTime) (#12660)
|
||||
- Clarifies experimental / parallel example on manual.rst (#12472)
|
||||
- Fix wrong section hierarchy in the manual (#12724)
|
||||
68
changelogs/changelog_1_0_6.md
Normal file
68
changelogs/changelog_1_0_6.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# v1.0.6 - 2020-01-24
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
### Fixed issues
|
||||
|
||||
- Fixed "Nim stdlib style issues with --styleCheck:error"
|
||||
([#12687](https://github.com/nim-lang/Nim/issues/12687))
|
||||
- Fixed "new(ref MyObject) doesn't work at compile time"
|
||||
([#12488](https://github.com/nim-lang/Nim/issues/12488))
|
||||
- Fixed "Accessing the wrong variant field in the VM does not trigger the appropriate error message"
|
||||
([#11727](https://github.com/nim-lang/Nim/issues/11727))
|
||||
- Fixed "orderedTable.del() crashes with unitialized table"
|
||||
([#12798](https://github.com/nim-lang/Nim/issues/12798))
|
||||
- Fixed "semfold bug with negative value * 0"
|
||||
([#12783](https://github.com/nim-lang/Nim/issues/12783))
|
||||
- Fixed "nimsuggest `use` command does not return all instances of symbol"
|
||||
([#12832](https://github.com/nim-lang/Nim/issues/12832))
|
||||
- Fixed "Codegen ICE in allPathsAsgnResult"
|
||||
([#12827](https://github.com/nim-lang/Nim/issues/12827))
|
||||
- Fixed "Static[T] + syntactic error in the for loop = compiler crash"
|
||||
([#12148](https://github.com/nim-lang/Nim/issues/12148))
|
||||
- Fixed "Incorrect unused import warning involving templates and tables"
|
||||
([#12885](https://github.com/nim-lang/Nim/issues/12885))
|
||||
- Fixed "regression(1.0.4): undeclared identifier: 'readLines'; plus another regression and bug"
|
||||
([#13013](https://github.com/nim-lang/Nim/issues/13013))
|
||||
- Fixed "`nim doc` treats `export localSymbol` incorrectly"
|
||||
([#13100](https://github.com/nim-lang/Nim/issues/13100))
|
||||
- Fixed "symbols not defined in the grammar"
|
||||
([#10665](https://github.com/nim-lang/Nim/issues/10665))
|
||||
- Fixed "nim-gdb is missing from all released packages"
|
||||
([#13104](https://github.com/nim-lang/Nim/issues/13104))
|
||||
- Fixed "[JS] Move is not defined"
|
||||
([#9674](https://github.com/nim-lang/Nim/issues/9674))
|
||||
- Fixed "Error: usage of 'isNil' is a user-defined error"
|
||||
([#11440](https://github.com/nim-lang/Nim/issues/11440))
|
||||
|
||||
|
||||
### Other bugfixes
|
||||
|
||||
- make addQuoted work on nimscript (#12717)
|
||||
- fix db_mysql getRow() when column is null error raised (#12806)
|
||||
- Fixed objects being erroneously zeroed out before object construction (#12814)
|
||||
- added cstrutils (#12858): fixed for 'csuCmpIgnoreStyle' error on hotcodereloading
|
||||
- Better clang_cl support (#12896)
|
||||
- fix cmdline bugs affecting nimBetterRun correctness (#12933)
|
||||
- fixes a bug that kept sugar.collect from working with for loop macros
|
||||
- Path substitution for --out and --outdir (#12796)
|
||||
- fix crash due to errant symbols in nim.cfg (#13073)
|
||||
- Allow `-o` option for `buildIndex` (#13037)
|
||||
- Deleted misplaced separator (#13085): Misplaced separator, which was constantly breaking compilation on Haiku OS, was deleted.
|
||||
- fixes an asyncftpclient bug; refs #13096
|
||||
- fix the ftp store function read the local file bug (#13108)
|
||||
- fix rtti sizeof for varargs in global scope (#13125)
|
||||
- Correctly remove a key from CountTable when it is set to zero.
|
||||
- fixes the distros.nim regression
|
||||
- fixes a critical times.nim bug reported on IRC
|
||||
- c_fflush() the rawWrite() buffer
|
||||
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
- Fixed "Documentation, Testament missing from "Tools available with Nim""
|
||||
([#12251](https://github.com/nim-lang/Nim/issues/12251))
|
||||
- Manual update: custom exceptions (#12847)
|
||||
- times/getClockStr(): fix mistake in doc
|
||||
- Fix typo and improve in code-block of 'lib/pure/parseutils.nim'
|
||||
@@ -1,535 +0,0 @@
|
||||
# v1.2.0 - 2020-04-02
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
- Added overloaded `strformat.fmt` macro that use specified characters as
|
||||
delimiter instead of '{' and '}'.
|
||||
- Added new procs in `tables.nim`: `OrderedTable.pop`, `CountTable.del`,
|
||||
`CountTable.pop`, `Table.pop`.
|
||||
- Added `strtabs.clear` overload that reuses the existing mode.
|
||||
- Added `browsers.osOpen` const alias for the operating system specific *"open"* command.
|
||||
- Added `sugar.dup` for turning in-place algorithms like `sort` and `shuffle`
|
||||
into operations that work on a copy of the data and return the mutated copy,
|
||||
like the existing `sorted` does.
|
||||
- Added `sugar.collect` that does comprehension for seq/set/table collections.
|
||||
- Added `sugar.capture` for capturing some local loop variables when creating a
|
||||
closure. This is an enhanced version of `closureScope`.
|
||||
- Added `typetraits.tupleLen` to get number of elements of a tuple/type tuple,
|
||||
and `typetraits.get` to get the ith element of a type tuple.
|
||||
- Added `typetraits.genericParams` to return a tuple of generic params from a
|
||||
generic instantiation.
|
||||
- `options` now treats `proc` like other pointer types, meaning `nil` proc variables
|
||||
are converted to `None`.
|
||||
- Added `os.normalizePathEnd` for additional path sanitization.
|
||||
- Added `times.fromUnixFloat,toUnixFloat`, sub-second resolution versions of
|
||||
`fromUnix`,`toUnixFloat`.
|
||||
- Added `wrapnils` module for chains of field-access and indexing where the LHS
|
||||
can be nil. This simplifies code by reducing need for if-else branches around
|
||||
intermediate maybe nil values. E.g. `echo ?.n.typ.kind`.
|
||||
- Added `minIndex`, `maxIndex` and `unzip` to the `sequtils` module.
|
||||
- Added `os.isRelativeTo` to tell whether a path is relative to another.
|
||||
- Added `resetOutputFormatters` to `unittest`.
|
||||
- Added `expectIdent` to the `macros` module.
|
||||
- Added `os.isValidFilename` that returns `true` if `filename` argument is valid
|
||||
for cross-platform use.
|
||||
- Added `times.isLeapDay`.
|
||||
- `base64` adds URL-Safe Base64, implements RFC-4648 Section-7.
|
||||
- Added a new module, `std / compilesettings` for querying the compiler about
|
||||
diverse configuration settings.
|
||||
- Added `net.getPeerCertificates` and `asyncnet.getPeerCertificates` for
|
||||
retrieving the verified certificate chain of the peer we are connected to
|
||||
through an SSL-wrapped `Socket`/`AsyncSocket`.
|
||||
- Added `browsers.openDefaultBrowser` without URL, implements IETF RFC-6694 Section-3.
|
||||
- Added `jsconsole.trace`, `jsconsole.table`, `jsconsole.exception` for JavaScript target.
|
||||
- Added `distinctBase` overload for values: `assert 12.MyInt.distinctBase == 12`
|
||||
- Added new module `std/stackframes`, in particular `setFrameMsg`, which enables
|
||||
custom runtime annotation of stackframes, see #13351 for examples.
|
||||
Turn on/off via `--stackTraceMsgs:on/off`.
|
||||
- Added `sequtils.countIt`, allowing for counting items using a predicate.
|
||||
- Added a `with` macro for easy function chaining that's available everywhere,
|
||||
there is no need to concern your APIs with returning the first argument
|
||||
to enable "chaining", instead use the dedicated macro `with` that
|
||||
was designed for it. For example:
|
||||
|
||||
```nim
|
||||
import std/with
|
||||
|
||||
type
|
||||
Foo = object
|
||||
col, pos: string
|
||||
|
||||
proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b)
|
||||
proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y)
|
||||
|
||||
var f: Foo
|
||||
with(f, setColor(2, 3, 4), setPosition(0.0, 1.0))
|
||||
echo f
|
||||
```
|
||||
|
||||
- `macros.newLit` now works for ref object types.
|
||||
- `macro pragmas` can now be used in type sections.
|
||||
- 5 new pragmas were added to Nim in order to make the upcoming tooling more
|
||||
convenient to use. Nim compiler checks these pragmas for syntax but otherwise
|
||||
ignores them. The pragmas are `requires`, `ensures`, `assume`, `assert`, `invariant`.
|
||||
- `system.writeFile` has been overloaded to also support `openarray[byte]`.
|
||||
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
|
||||
and only returns once all pending async operations are guaranteed to have completed.
|
||||
- `sequtils.zip` now returns a sequence of anonymous tuples i.e. those tuples
|
||||
now do not have fields named "a" and "b".
|
||||
- `distinctBase` has been moved from `sugar` to `typetraits` and now it is
|
||||
implemented as compiler type trait instead of macro. `distinctBase` in sugar
|
||||
module is now deprecated.
|
||||
- `CountTable.mget` has been removed from `tables.nim`. It didn't work, and it
|
||||
was an oversight to be included in v1.0.
|
||||
- `tables.merge(CountTable, CountTable): CountTable` has been removed.
|
||||
It didn't work well together with the existing inplace version of the same proc
|
||||
(`tables.merge(var CountTable, CountTable)`).
|
||||
It was an oversight to be included in v1.0.
|
||||
- `asyncdispatch.drain` now consistently uses the passed timeout value for all
|
||||
iterations of the event loop, and not just the first iteration.
|
||||
This is more consistent with the other asyncdispatch APIs, and allows
|
||||
`asyncdispatch.drain` to be more efficient.
|
||||
- `base64.encode` and `base64.decode` were made faster by about 50%.
|
||||
- `htmlgen` adds [MathML](https://wikipedia.org/wiki/MathML) support
|
||||
(ISO 40314).
|
||||
- `macros.eqIdent` is now invariant to export markers and backtick quotes.
|
||||
- `htmlgen.html` allows `lang` in the `<html>` tag and common valid attributes.
|
||||
- `macros.basename` and `basename=` got support for `PragmaExpr`,
|
||||
so that an expression like `MyEnum {.pure.}` is handled correctly.
|
||||
- `httpclient.maxredirects` changed from `int` to `Natural`, because negative values
|
||||
serve no purpose whatsoever.
|
||||
- `httpclient.newHttpClient` and `httpclient.newAsyncHttpClient` added `headers`
|
||||
argument to set initial HTTP Headers, instead of a hardcoded empty `newHttpHeader()`.
|
||||
- `parseutils.parseUntil` has now a different behaviour if the `until` parameter is
|
||||
empty. This was required for intuitive behaviour of the strscans module
|
||||
(see bug #13605).
|
||||
- `strutils.formatFloat` with `precision = 0` has the same behavior in all
|
||||
backends, and it is compatible with Python's behavior,
|
||||
e.g. `formatFloat(3.14159, precision = 0)` is now `3`, not `3.`.
|
||||
- `times.parse` now only uses input to compute its result, and not `now`:
|
||||
`parse("2020", "YYYY", utc())` is now `2020-01-01T00:00:00Z` instead of
|
||||
`2020-03-02T00:00:00Z` if run on 03-02; it also doesn't crash anymore when
|
||||
used on 29th, 30th, 31st of each month.
|
||||
- `httpcore.==(string, HttpCode)` is now deprecated due to lack of practical
|
||||
usage. The `$` operator can be used to obtain the string form of `HttpCode`
|
||||
for comparison if desired.
|
||||
- `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs.
|
||||
- `os.walkDir` and `os.walkDirRec` now have new flag, `checkDir` (default: false).
|
||||
If it is set to true, it will throw if input dir is invalid instead of a noop
|
||||
(which is the default behaviour, as it was before this change),
|
||||
`os.walkDirRec` only throws if top-level dir is invalid, but ignores errors for
|
||||
subdirs, otherwise it would be impossible to resume iteration.
|
||||
- The `FD` variant of `selector.unregister` for `ioselector_epoll` and
|
||||
`ioselector_select` now properly handle the `Event.User` select event type.
|
||||
- `joinPath` path normalization when `/` is the first argument works correctly:
|
||||
`assert "/" / "/a" == "/a"`. Fixed the edge case: `assert "" / "" == ""`.
|
||||
- `xmltree` now adds indentation consistently to child nodes for any number
|
||||
of children nodes.
|
||||
- `os.splitPath()` behavior synchronized with `os.splitFile()` to return "/"
|
||||
as the dir component of `/root_sub_dir` instead of the empty string.
|
||||
- The deprecated `lc` macro has been removed from `sugar`. It is now replaced
|
||||
with the more powerful `collect` macro.
|
||||
- `os.relativePath("foo", "foo")` is now `"."`, not `""`, as `""` means invalid
|
||||
path and shouldn't be conflated with `"."`; use `-d:nimOldRelativePathBehavior`
|
||||
to restore the old behavior.
|
||||
- `os.joinPath(a, b)` now honors trailing slashes in `b` (or `a` if `b` = "").
|
||||
- `base64.encode` no longer supports `lineLen` and `newLine`.
|
||||
Use `base64.encodeMime` instead.
|
||||
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- `net.newContext` now performs SSL Certificate checking on Linux and OSX.
|
||||
Define `nimDisableCertificateValidation` to disable it globally.
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- An `align` pragma can now be used for variables and object fields, similar
|
||||
to the `alignas` declaration modifier in C/C++.
|
||||
- The `=sink` type bound operator is now optional. The compiler can now use a
|
||||
combination of `=destroy` and `copyMem` to move objects efficiently.
|
||||
- Unsigned integer operators have been fixed to allow promotion of the first operand.
|
||||
- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim
|
||||
versions. The documentation was improved to acknowledge this special case.
|
||||
See https://github.com/nim-lang/RFCs/issues/175 for more details.
|
||||
- There is a new syntax for lvalue references: `var b {.byaddr.} = expr` enabled by
|
||||
`import std/decls`.
|
||||
- `var a {.foo.}: MyType = expr` now lowers to `foo(a, MyType, expr)` for
|
||||
non-builtin pragmas, enabling things like lvalue references (see `decls.byaddr`).
|
||||
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- The generated JS code uses spaces, instead of mixing spaces and tabs.
|
||||
- The Nim compiler now supports the ``--asm`` command option for easier
|
||||
inspection of the produced assembler code.
|
||||
- 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 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
|
||||
errors like `IndexError` or `OverflowError` into fatal errors that **cannot**
|
||||
be caught via Nim's `try` statement. `--panics:on` can improve the
|
||||
runtime efficiency and code size of your program significantly.
|
||||
- The compiler now warns about inheriting directly from `system.Exception` as
|
||||
this is **very bad** style. You should inherit from `ValueError`, `IOError`,
|
||||
`OSError` or from a different specific exception type that inherits from
|
||||
`CatchableError` and cannot be confused with a `Defect`.
|
||||
- The error reporting for Nim's effect system has been improved.
|
||||
- Implicit conversions for `const` behave correctly now, meaning that code like
|
||||
`const SOMECONST = 0.int; procThatTakesInt32(SOMECONST)` will be illegal now.
|
||||
Simply write `const SOMECONST = 0` instead.
|
||||
- The `{.dynlib.}` pragma is now required for exporting symbols when making
|
||||
shared objects on POSIX and macOS, which make it consistent with the behavior
|
||||
on Windows.
|
||||
- The compiler is now more strict about type conversions concerning proc
|
||||
types: Type conversions cannot be used to hide `.raise` effects or side
|
||||
effects, instead a `cast` must be used. With the flag `--useVersion:1.0` the
|
||||
old behaviour is emulated.
|
||||
- The Nim compiler now implements a faster way to detect overflows based
|
||||
on GCC's `__builtin_sadd_overflow` family of functions. (Clang also
|
||||
supports these). Some versions of GCC lack this feature and unfortunately
|
||||
we cannot detect this case reliably. So if you get compilation errors like
|
||||
"undefined reference to `__builtin_saddll_overflow`" compile your programs
|
||||
with `-d:nimEmulateOverflowChecks`.
|
||||
|
||||
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed "`nimgrep --nocolor` is ignored on posix; should be instead: `--nimgrep --color=[auto]|true|false`"
|
||||
([#7591](https://github.com/nim-lang/Nim/issues/7591))
|
||||
- Fixed "Runtime index on const array (of converted obj) causes C-compiler error"
|
||||
([#10514](https://github.com/nim-lang/Nim/issues/10514))
|
||||
- Fixed "windows x86 with vcc compile error with "asmNoStackFrame""
|
||||
([#12298](https://github.com/nim-lang/Nim/issues/12298))
|
||||
- Fixed "[TODO] regression: Error: Locks requires --threads:on option"
|
||||
([#12330](https://github.com/nim-lang/Nim/issues/12330))
|
||||
- Fixed "Add --cc option to --help or --fullhelp output"
|
||||
([#12010](https://github.com/nim-lang/Nim/issues/12010))
|
||||
- Fixed "questionable `csize` definition in `system.nim`"
|
||||
([#12187](https://github.com/nim-lang/Nim/issues/12187))
|
||||
- Fixed "os.getAppFilename() returns incorrect results on OpenBSD"
|
||||
([#12389](https://github.com/nim-lang/Nim/issues/12389))
|
||||
- Fixed "HashSet[uint64] slow insertion depending on values"
|
||||
([#11764](https://github.com/nim-lang/Nim/issues/11764))
|
||||
- Fixed "Differences between compiling 'classic call syntax' vs 'method call syntax' ."
|
||||
([#12453](https://github.com/nim-lang/Nim/issues/12453))
|
||||
- Fixed "c -d:nodejs --> SIGSEGV: Illegal storage access"
|
||||
([#12502](https://github.com/nim-lang/Nim/issues/12502))
|
||||
- Fixed "Closure iterator crashes on --newruntime due to "dangling references""
|
||||
([#12443](https://github.com/nim-lang/Nim/issues/12443))
|
||||
- Fixed "No `=destroy` for elements of closure environments other than for latest devel --gc:destructors"
|
||||
([#12577](https://github.com/nim-lang/Nim/issues/12577))
|
||||
- Fixed "strutils:formatBiggestFloat() gives different results in JS mode"
|
||||
([#8242](https://github.com/nim-lang/Nim/issues/8242))
|
||||
- Fixed "Regression (devel): the new `csize_t` definition isn't consistently used, nor tested thoroughly..."
|
||||
([#12597](https://github.com/nim-lang/Nim/issues/12597))
|
||||
- Fixed "tables.take() is defined only for `Table` and missed for other table containers"
|
||||
([#12519](https://github.com/nim-lang/Nim/issues/12519))
|
||||
- Fixed "`pthread_key_t` errors on OpenBSD"
|
||||
([#12135](https://github.com/nim-lang/Nim/issues/12135))
|
||||
- Fixed "newruntime: simple seq pop at ct results in compile error"
|
||||
([#12644](https://github.com/nim-lang/Nim/issues/12644))
|
||||
- Fixed "[Windows] finish.exe C:\Users\<USERNAME>\.nimble\bin is not in your PATH environment variable."
|
||||
([#12319](https://github.com/nim-lang/Nim/issues/12319))
|
||||
- Fixed "Error with strformat + asyncdispatch + const"
|
||||
([#12612](https://github.com/nim-lang/Nim/issues/12612))
|
||||
- Fixed "MultipartData needs $"
|
||||
([#11863](https://github.com/nim-lang/Nim/issues/11863))
|
||||
- Fixed "Nim stdlib style issues with --styleCheck:error"
|
||||
([#12687](https://github.com/nim-lang/Nim/issues/12687))
|
||||
- Fixed "new $nimbleDir path substitution yields unexpected search paths"
|
||||
([#12767](https://github.com/nim-lang/Nim/issues/12767))
|
||||
- Fixed "Regression: inlined procs now get multiple rounds of destructor injection"
|
||||
([#12766](https://github.com/nim-lang/Nim/issues/12766))
|
||||
- Fixed "newruntime: compiler generates defective code"
|
||||
([#12669](https://github.com/nim-lang/Nim/issues/12669))
|
||||
- Fixed "broken windows modules path handling because of 'os.relativePath' breaking changes"
|
||||
([#12734](https://github.com/nim-lang/Nim/issues/12734))
|
||||
- Fixed "for loop tuple syntax not rendered correctly"
|
||||
([#12740](https://github.com/nim-lang/Nim/issues/12740))
|
||||
- Fixed "Crash when trying to use `type.name[0]`"
|
||||
([#12804](https://github.com/nim-lang/Nim/issues/12804))
|
||||
- Fixed "Enums should be considered Trivial types in Atomics"
|
||||
([#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 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))
|
||||
- Fixed "@[] is a problem for --gc:destructors"
|
||||
([#12820](https://github.com/nim-lang/Nim/issues/12820))
|
||||
- Fixed "Codegen ICE in allPathsAsgnResult"
|
||||
([#12827](https://github.com/nim-lang/Nim/issues/12827))
|
||||
- Fixed "seq[Object with ref and destructor type] doesn't work in old runtime"
|
||||
([#12882](https://github.com/nim-lang/Nim/issues/12882))
|
||||
- Fixed "Destructor not invoked because it is instantiated too late, old runtime"
|
||||
([#12883](https://github.com/nim-lang/Nim/issues/12883))
|
||||
- Fixed "The collect macro does not handle if/case correctly"
|
||||
([#12874](https://github.com/nim-lang/Nim/issues/12874))
|
||||
- Fixed "allow typed/untyped params in magic procs (even if not in stdlib)"
|
||||
([#12911](https://github.com/nim-lang/Nim/issues/12911))
|
||||
- Fixed "ARC/newruntime memory corruption"
|
||||
([#12899](https://github.com/nim-lang/Nim/issues/12899))
|
||||
- Fixed "tasyncclosestall.nim still flaky test: Address already in use"
|
||||
([#12919](https://github.com/nim-lang/Nim/issues/12919))
|
||||
- Fixed "newruntime and computed goto: variables inside the loop are in generated code uninitialised"
|
||||
([#12785](https://github.com/nim-lang/Nim/issues/12785))
|
||||
- Fixed "osx: dsymutil needs to be called for debug builds to keep debug info"
|
||||
([#12735](https://github.com/nim-lang/Nim/issues/12735))
|
||||
- Fixed "codegen ICE with ref objects, gc:destructors"
|
||||
([#12826](https://github.com/nim-lang/Nim/issues/12826))
|
||||
- Fixed "mutable iterator cannot yield named tuples"
|
||||
([#12945](https://github.com/nim-lang/Nim/issues/12945))
|
||||
- Fixed "parsecfg stores "\r\n" line breaks just as "\n""
|
||||
([#12970](https://github.com/nim-lang/Nim/issues/12970))
|
||||
- Fixed "db_postgres.getValue issues warning when no rows found"
|
||||
([#12973](https://github.com/nim-lang/Nim/issues/12973))
|
||||
- Fixed "ARC: Unpacking tuple with seq causes segfault"
|
||||
([#12989](https://github.com/nim-lang/Nim/issues/12989))
|
||||
- Fixed "ARC/newruntime: strutils.join on seq with only empty strings causes segfault"
|
||||
([#12965](https://github.com/nim-lang/Nim/issues/12965))
|
||||
- Fixed "regression (1.0.4): `{.push exportc.}` wrongly affects generic instantiations, causing codegen errors"
|
||||
([#12985](https://github.com/nim-lang/Nim/issues/12985))
|
||||
- Fixed "cdt, crash with --gc:arc, works fine with default gc"
|
||||
([#12978](https://github.com/nim-lang/Nim/issues/12978))
|
||||
- Fixed "ARC: No indexError thrown on out-of-bound seq access, SIGSEGV instead"
|
||||
([#12961](https://github.com/nim-lang/Nim/issues/12961))
|
||||
- Fixed "ARC/async: Returning in a try-block results in wrong codegen"
|
||||
([#12956](https://github.com/nim-lang/Nim/issues/12956))
|
||||
- Fixed "asm keyword is generating wrong output C code when --cc:tcc"
|
||||
([#12988](https://github.com/nim-lang/Nim/issues/12988))
|
||||
- Fixed "Destructor not invoked"
|
||||
([#13026](https://github.com/nim-lang/Nim/issues/13026))
|
||||
- Fixed "ARC/newruntime: Adding inherited var ref object to seq with base type causes segfault"
|
||||
([#12964](https://github.com/nim-lang/Nim/issues/12964))
|
||||
- Fixed "Style check error with JS compiler target"
|
||||
([#13032](https://github.com/nim-lang/Nim/issues/13032))
|
||||
- Fixed "regression(1.0.4): undeclared identifier: 'readLines'; plus another regression and bug"
|
||||
([#13013](https://github.com/nim-lang/Nim/issues/13013))
|
||||
- Fixed "regression(1.04) `invalid pragma: since` with nim js"
|
||||
([#12996](https://github.com/nim-lang/Nim/issues/12996))
|
||||
- Fixed "Sink to MemMove optimization in injectdestructors"
|
||||
([#13002](https://github.com/nim-lang/Nim/issues/13002))
|
||||
- Fixed "--gc:arc: `catch` doesn't work with exception subclassing"
|
||||
([#13072](https://github.com/nim-lang/Nim/issues/13072))
|
||||
- Fixed "nim c --gc:arc --exceptions:{setjmp,goto} incorrectly handles raise; `nim cpp --gc:arc` is ok"
|
||||
([#13070](https://github.com/nim-lang/Nim/issues/13070))
|
||||
- Fixed "typetraits feature request - get subtype of a generic type"
|
||||
([#6454](https://github.com/nim-lang/Nim/issues/6454))
|
||||
- Fixed "CountTable inconsistencies between keys() and len() after setting value to 0"
|
||||
([#12813](https://github.com/nim-lang/Nim/issues/12813))
|
||||
- Fixed "{.align.} pragma is not applied if there is a generic field"
|
||||
([#13122](https://github.com/nim-lang/Nim/issues/13122))
|
||||
- Fixed "ARC, finalizer, allow rebinding the same function multiple times"
|
||||
([#13112](https://github.com/nim-lang/Nim/issues/13112))
|
||||
- Fixed "`nim doc` treats `export localSymbol` incorrectly"
|
||||
([#13100](https://github.com/nim-lang/Nim/issues/13100))
|
||||
- Fixed "--gc:arc SIGSEGV (double free?)"
|
||||
([#13119](https://github.com/nim-lang/Nim/issues/13119))
|
||||
- Fixed "codegen bug with arc"
|
||||
([#13105](https://github.com/nim-lang/Nim/issues/13105))
|
||||
- Fixed "symbols not defined in the grammar"
|
||||
([#10665](https://github.com/nim-lang/Nim/issues/10665))
|
||||
- Fixed "[JS] Move is not defined"
|
||||
([#9674](https://github.com/nim-lang/Nim/issues/9674))
|
||||
- Fixed "[TODO] pathutils.`/` can return invalid AbsoluteFile"
|
||||
([#13121](https://github.com/nim-lang/Nim/issues/13121))
|
||||
- Fixed "regression(1.04) `nim doc main.nim` generates broken html (no css)"
|
||||
([#12998](https://github.com/nim-lang/Nim/issues/12998))
|
||||
- Fixed "Wrong supportsCopyMem on string in type section"
|
||||
([#13095](https://github.com/nim-lang/Nim/issues/13095))
|
||||
- Fixed "Arc, finalizer, out of memory"
|
||||
([#13157](https://github.com/nim-lang/Nim/issues/13157))
|
||||
- Fixed "`--genscript` messes up nimcache and future nim invocations"
|
||||
([#13144](https://github.com/nim-lang/Nim/issues/13144))
|
||||
- Fixed "--gc:arc with --exceptions:goto for "nim c" generate invalid c code"
|
||||
([#13186](https://github.com/nim-lang/Nim/issues/13186))
|
||||
- Fixed "[regression] duplicate member `_i1` codegen bug"
|
||||
([#13195](https://github.com/nim-lang/Nim/issues/13195))
|
||||
- Fixed "RTree investigations with valgrind for --gc:arc"
|
||||
([#13110](https://github.com/nim-lang/Nim/issues/13110))
|
||||
- Fixed "relativePath("foo", ".") returns wrong path"
|
||||
([#13211](https://github.com/nim-lang/Nim/issues/13211))
|
||||
- Fixed "asyncftpclient - problem with welcome.msg"
|
||||
([#4684](https://github.com/nim-lang/Nim/issues/4684))
|
||||
- Fixed "Unclear error message, lowest priority"
|
||||
([#13256](https://github.com/nim-lang/Nim/issues/13256))
|
||||
- Fixed "Channel messages are corrupted"
|
||||
([#13219](https://github.com/nim-lang/Nim/issues/13219))
|
||||
- Fixed "Codegen bug with exportc and case objects"
|
||||
([#13281](https://github.com/nim-lang/Nim/issues/13281))
|
||||
- Fixed "[bugfix] fix #11590: c compiler warnings silently ignored, giving undefined behavior"
|
||||
([#11591](https://github.com/nim-lang/Nim/issues/11591))
|
||||
- Fixed "[CI] tnetdial flaky test"
|
||||
([#13132](https://github.com/nim-lang/Nim/issues/13132))
|
||||
- Fixed "Cross-Compiling with -d:mingw fails to locate compiler under OSX"
|
||||
([#10717](https://github.com/nim-lang/Nim/issues/10717))
|
||||
- Fixed "`nim doc --project` broken with imports below main project file or duplicate names"
|
||||
([#13150](https://github.com/nim-lang/Nim/issues/13150))
|
||||
- Fixed "regression: isNamedTuple(MyGenericTuple[int]) is false, should be true"
|
||||
([#13349](https://github.com/nim-lang/Nim/issues/13349))
|
||||
- Fixed "--gc:arc codegen bug copying objects bound to C structs with missing C struct fields"
|
||||
([#13269](https://github.com/nim-lang/Nim/issues/13269))
|
||||
- Fixed "write requires conversion to string"
|
||||
([#13182](https://github.com/nim-lang/Nim/issues/13182))
|
||||
- Fixed "Some remarks to stdlib documentation"
|
||||
([#13352](https://github.com/nim-lang/Nim/issues/13352))
|
||||
- Fixed "a `check` in unittest generated by template doesn't show actual value"
|
||||
([#6736](https://github.com/nim-lang/Nim/issues/6736))
|
||||
- Fixed "Implicit return with case expression fails with 'var' return."
|
||||
([#3339](https://github.com/nim-lang/Nim/issues/3339))
|
||||
- Fixed "Segfault with closure on arc"
|
||||
([#13314](https://github.com/nim-lang/Nim/issues/13314))
|
||||
- Fixed "[Macro] Crash on malformed case statement with multiple else"
|
||||
([#13255](https://github.com/nim-lang/Nim/issues/13255))
|
||||
- Fixed "regression: `echo 'discard' | nim c -r -` generates a file '-' ; `-` should be treated specially"
|
||||
([#13374](https://github.com/nim-lang/Nim/issues/13374))
|
||||
- Fixed "on OSX, debugging (w gdb or lldb) a nim program crashes at the 1st call to `execCmdEx`"
|
||||
([#9634](https://github.com/nim-lang/Nim/issues/9634))
|
||||
- Fixed "Internal error in getTypeDescAux"
|
||||
([#13378](https://github.com/nim-lang/Nim/issues/13378))
|
||||
- Fixed "gc:arc mode breaks tuple let"
|
||||
([#13368](https://github.com/nim-lang/Nim/issues/13368))
|
||||
- Fixed "Nim compiler hangs for certain C/C++ compiler errors"
|
||||
([#8648](https://github.com/nim-lang/Nim/issues/8648))
|
||||
- Fixed "htmlgen does not support `data-*` attributes"
|
||||
([#13444](https://github.com/nim-lang/Nim/issues/13444))
|
||||
- Fixed "[gc:arc] setLen will cause string not to be null-terminated."
|
||||
([#13457](https://github.com/nim-lang/Nim/issues/13457))
|
||||
- Fixed "joinPath("", "") is "/" ; should be """
|
||||
([#13455](https://github.com/nim-lang/Nim/issues/13455))
|
||||
- Fixed "[CI] flaky test on windows: tests/osproc/texitcode.nim"
|
||||
([#13449](https://github.com/nim-lang/Nim/issues/13449))
|
||||
- Fixed "Casting to float32 on NimVM is broken"
|
||||
([#13479](https://github.com/nim-lang/Nim/issues/13479))
|
||||
- Fixed "`--hints:off` doesn't work (doesn't override ~/.config/nim.cfg)"
|
||||
([#8312](https://github.com/nim-lang/Nim/issues/8312))
|
||||
- Fixed "joinPath("", "") is "/" ; should be """
|
||||
([#13455](https://github.com/nim-lang/Nim/issues/13455))
|
||||
- Fixed "tables.values is broken"
|
||||
([#13496](https://github.com/nim-lang/Nim/issues/13496))
|
||||
- Fixed "global user config can override project specific config"
|
||||
([#9405](https://github.com/nim-lang/Nim/issues/9405))
|
||||
- Fixed "Non deterministic macros and id consistency problem"
|
||||
([#12627](https://github.com/nim-lang/Nim/issues/12627))
|
||||
- Fixed "try expression doesn't work with return on expect branch"
|
||||
([#13490](https://github.com/nim-lang/Nim/issues/13490))
|
||||
- Fixed "CI will break every 4 years on feb 28: times doesn't handle leap years properly"
|
||||
([#13543](https://github.com/nim-lang/Nim/issues/13543))
|
||||
- Fixed "[minor] `nimgrep --word` doesn't work with operators (eg misses `1 +% 2`)"
|
||||
([#13528](https://github.com/nim-lang/Nim/issues/13528))
|
||||
- Fixed "`as` is usable as infix operator but its existence and precedence are not documented"
|
||||
([#13409](https://github.com/nim-lang/Nim/issues/13409))
|
||||
- Fixed "JSON unmarshalling drops seq's items"
|
||||
([#13531](https://github.com/nim-lang/Nim/issues/13531))
|
||||
- Fixed "os.joinPath returns wrong path when head ends '\' or '/' and tail starts '..'."
|
||||
([#13579](https://github.com/nim-lang/Nim/issues/13579))
|
||||
- Fixed "Block-local types with the same name lead to bad codegen (sighashes regression)"
|
||||
([#5170](https://github.com/nim-lang/Nim/issues/5170))
|
||||
- Fixed "tuple codegen error"
|
||||
([#12704](https://github.com/nim-lang/Nim/issues/12704))
|
||||
- Fixed "newHttpHeaders does not accept repeated headers"
|
||||
([#13573](https://github.com/nim-lang/Nim/issues/13573))
|
||||
- Fixed "regression: --incremental:on fails on simplest example"
|
||||
([#13319](https://github.com/nim-lang/Nim/issues/13319))
|
||||
- Fixed "strscan can't get value of last element in format"
|
||||
([#13605](https://github.com/nim-lang/Nim/issues/13605))
|
||||
- Fixed "hashes_examples crashes with "Bus Error" (unaligned access) on sparc64"
|
||||
([#12508](https://github.com/nim-lang/Nim/issues/12508))
|
||||
- Fixed "gc:arc bug with re-used `seq[T]`"
|
||||
([#13596](https://github.com/nim-lang/Nim/issues/13596))
|
||||
- Fixed "`raise CatchableError` is broken with --gc:arc when throwing inside a proc"
|
||||
([#13599](https://github.com/nim-lang/Nim/issues/13599))
|
||||
- Fixed "cpp --gc:arc --exceptions:goto fails to raise with discard"
|
||||
([#13436](https://github.com/nim-lang/Nim/issues/13436))
|
||||
- Fixed "terminal doesn't compile with -d:useWinAnsi"
|
||||
([#13607](https://github.com/nim-lang/Nim/issues/13607))
|
||||
- Fixed "Parsing "sink ptr T" - region needs to be an object type"
|
||||
([#12757](https://github.com/nim-lang/Nim/issues/12757))
|
||||
- Fixed "gc:arc + threads:on + closures compilation error"
|
||||
([#13519](https://github.com/nim-lang/Nim/issues/13519))
|
||||
- Fixed "[ARC] segmentation fault"
|
||||
([#13240](https://github.com/nim-lang/Nim/issues/13240))
|
||||
- Fixed "times.toDateTime buggy on 29th, 30th and 31th of each month"
|
||||
([#13558](https://github.com/nim-lang/Nim/issues/13558))
|
||||
- Fixed "Deque misbehaves on VM"
|
||||
([#13310](https://github.com/nim-lang/Nim/issues/13310))
|
||||
- Fixed "Nimscript listFiles should throw exception when path is not found"
|
||||
([#12676](https://github.com/nim-lang/Nim/issues/12676))
|
||||
- Fixed "koch boot fails if even an empty config.nims is present in ~/.config/nims/ [devel regression]"
|
||||
([#13633](https://github.com/nim-lang/Nim/issues/13633))
|
||||
- Fixed "nim doc generates lots of false positive LockLevel warnings"
|
||||
([#13218](https://github.com/nim-lang/Nim/issues/13218))
|
||||
- Fixed "Arrays are passed by copy to iterators, causing crashes, unnecessary allocations and slowdowns"
|
||||
([#12747](https://github.com/nim-lang/Nim/issues/12747))
|
||||
- Fixed "Range types always uses signed integer as a base type"
|
||||
([#13646](https://github.com/nim-lang/Nim/issues/13646))
|
||||
- Fixed "Generate c code cannot compile with recent devel version"
|
||||
([#13645](https://github.com/nim-lang/Nim/issues/13645))
|
||||
- Fixed "[regression] VM: Error: cannot convert -1 to uint64"
|
||||
([#13661](https://github.com/nim-lang/Nim/issues/13661))
|
||||
- Fixed "Spurious raiseException(Exception) detected"
|
||||
([#13654](https://github.com/nim-lang/Nim/issues/13654))
|
||||
- Fixed "gc:arc memory leak"
|
||||
([#13659](https://github.com/nim-lang/Nim/issues/13659))
|
||||
- Fixed "Error: cannot convert -1 to uint (inside tuples)"
|
||||
([#13671](https://github.com/nim-lang/Nim/issues/13671))
|
||||
- Fixed "strformat issue with --gc:arc"
|
||||
([#13622](https://github.com/nim-lang/Nim/issues/13622))
|
||||
- Fixed "astToStr doesn't work inside generics"
|
||||
([#13524](https://github.com/nim-lang/Nim/issues/13524))
|
||||
- Fixed "oswalkdir.walkDirRec wont return folders"
|
||||
([#11458](https://github.com/nim-lang/Nim/issues/11458))
|
||||
- Fixed "`echo 'echo 1' | nim c -r -` silently gives wrong results (nimBetterRun not updated for stdin)"
|
||||
([#13412](https://github.com/nim-lang/Nim/issues/13412))
|
||||
- Fixed "gc:arc destroys the global variable accidentally."
|
||||
([#13691](https://github.com/nim-lang/Nim/issues/13691))
|
||||
- Fixed "[minor] sigmatch errors should be sorted, for reproducible errors"
|
||||
([#13538](https://github.com/nim-lang/Nim/issues/13538))
|
||||
- Fixed "Exception when converting csize to clong"
|
||||
([#13698](https://github.com/nim-lang/Nim/issues/13698))
|
||||
- Fixed "ARC: variables are no copied on the thread spawn causing crashes"
|
||||
([#13708](https://github.com/nim-lang/Nim/issues/13708))
|
||||
- Fixed "Illegal distinct seq causes compiler crash"
|
||||
([#13720](https://github.com/nim-lang/Nim/issues/13720))
|
||||
- Fixed "cyclic seq definition crashes the compiler"
|
||||
([#13715](https://github.com/nim-lang/Nim/issues/13715))
|
||||
- Fixed "Iterator with openArray parameter make the argument evaluated many times"
|
||||
([#13417](https://github.com/nim-lang/Nim/issues/13417))
|
||||
- Fixed "net/asyncnet: Unable to access peer's certificate chain"
|
||||
([#13299](https://github.com/nim-lang/Nim/issues/13299))
|
||||
- Fixed "Accidentally "SIGSEGV: Illegal storage access" error after arc optimizations (#13325)"
|
||||
([#13709](https://github.com/nim-lang/Nim/issues/13709))
|
||||
- Fixed "Base64 Regression"
|
||||
([#13722](https://github.com/nim-lang/Nim/issues/13722))
|
||||
- Fixed "A regression (?) with --gc:arc and repr"
|
||||
([#13731](https://github.com/nim-lang/Nim/issues/13731))
|
||||
- Fixed "Internal compiler error when using the new variable pragmas"
|
||||
([#13737](https://github.com/nim-lang/Nim/issues/13737))
|
||||
- Fixed "bool conversion produces vcc 2019 warning at cpp compilation stage"
|
||||
([#13744](https://github.com/nim-lang/Nim/issues/13744))
|
||||
- Fixed "Compiler "does not detect" a type recursion error in the wrong code, remaining frozen"
|
||||
([#13763](https://github.com/nim-lang/Nim/issues/13763))
|
||||
- Fixed "[minor] regression: `Foo[0.0] is Foo[-0.0]` is now false"
|
||||
([#13730](https://github.com/nim-lang/Nim/issues/13730))
|
||||
- Fixed "`nim doc` - only whitespace on first line causes segfault"
|
||||
([#13631](https://github.com/nim-lang/Nim/issues/13631))
|
||||
- Fixed "hashset regression"
|
||||
([#13794](https://github.com/nim-lang/Nim/issues/13794))
|
||||
- Fixed "`os.getApplFreebsd` could return incorrect paths in the case of a long path"
|
||||
([#13806](https://github.com/nim-lang/Nim/issues/13806))
|
||||
- Fixed "Destructors are not inherited"
|
||||
([#13810](https://github.com/nim-lang/Nim/issues/13810))
|
||||
- Fixed "io.readLines AssertionError on devel"
|
||||
([#13829](https://github.com/nim-lang/Nim/issues/13829))
|
||||
- Fixed "exceptions:goto accidentally reset the variable during exception handling"
|
||||
([#13782](https://github.com/nim-lang/Nim/issues/13782))
|
||||
@@ -1,874 +0,0 @@
|
||||
# v1.4.0 - 2020-10-16
|
||||
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
- Added some enhancements to `std/jsonutils` module.
|
||||
* Added a possibility to deserialize JSON arrays directly to `HashSet` and
|
||||
`OrderedSet` types and respectively to serialize those types to JSON arrays
|
||||
via `jsonutils.fromJson` and `jsonutils.toJson` procedures.
|
||||
* Added a possibility to deserialize JSON `null` objects to Nim option objects
|
||||
and respectively to serialize Nim option object to JSON object if `isSome`
|
||||
or to JSON null object if `isNone` via `jsonutils.fromJson` and
|
||||
`jsonutils.toJson` procedures.
|
||||
* Added a `Joptions` parameter to `jsonutils.fromJson` currently
|
||||
containing two boolean options `allowExtraKeys` and `allowMissingKeys`.
|
||||
- If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is
|
||||
not required to have a field for every JSON key.
|
||||
- If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is
|
||||
allowed to have fields without corresponding JSON keys.
|
||||
- Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement.
|
||||
- Added `tryInsert`,`insert` procs to `db_*` libs which accept primary key column name.
|
||||
- Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text.
|
||||
- `uri` module now implements RFC-2397.
|
||||
- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
|
||||
to the `dom` module for the JavaScript target.
|
||||
- The default hash for `Ordinal` has changed to something more bit-scrambling.
|
||||
`import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old
|
||||
one in an instantiation context while `-d:nimIntHash1` recovers it globally.
|
||||
- `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads.
|
||||
- File handles created from high-level abstractions in the stdlib will no longer
|
||||
be inherited by child processes. In particular, these modules are affected:
|
||||
`asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`.
|
||||
|
||||
For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable`
|
||||
flag has been added to all `proc`s that create sockets, allowing the user to
|
||||
control whether the resulting socket is inheritable. This flag is provided to
|
||||
ease the writing of multi-process servers, where sockets inheritance is
|
||||
desired.
|
||||
|
||||
For a transition period, define `nimInheritHandles` to enable file handle
|
||||
inheritance by default. This flag does **not** affect the `selectors` module
|
||||
due to the differing semantics between operating systems.
|
||||
|
||||
`asyncdispatch.setInheritable`, `system.setInheritable` and
|
||||
`nativesockets.setInheritable` are also introduced for setting file handle or
|
||||
socket inheritance. Not all platforms have these `proc`s defined.
|
||||
|
||||
- The file descriptors created for internal bookkeeping by `ioselector_kqueue`
|
||||
and `ioselector_epoll` will no longer be leaked to child processes.
|
||||
|
||||
- `strutils.formatFloat` with `precision = 0` has been restored to the version
|
||||
1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)`
|
||||
is now `3.`, not `3`.
|
||||
- Added `commonPrefixLen` to `critbits`.
|
||||
|
||||
- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results
|
||||
(see #13222); instead they now use `getCurrentDir` to resolve those cases,
|
||||
and this can now throw in edge cases where `getCurrentDir` throws.
|
||||
`relativePath` also now works for js with `-d:nodejs`.
|
||||
|
||||
- JavaScript and NimScript standard library changes: `streams.StringStream` is
|
||||
now supported in JavaScript, with the limitation that any buffer `pointer`s
|
||||
used must be castable to `ptr string`, any incompatible pointer type will not
|
||||
work. The `lexbase` and `streams` modules used to fail to compile on
|
||||
NimScript due to a bug, but this has been fixed.
|
||||
|
||||
The following modules now compile on both JS and NimScript: `parsecsv`,
|
||||
`parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally
|
||||
supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for
|
||||
NimScript: `json`, `parsejson`, `strtabs` and `unidecode`.
|
||||
|
||||
- Added `streams.readStr` and `streams.peekStr` overloads to
|
||||
accept an existing string to modify, which avoids memory
|
||||
allocations, similar to `streams.readLine` (#13857).
|
||||
|
||||
- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality.
|
||||
|
||||
- `dollars.$` now works for unsigned ints with `nim js`.
|
||||
|
||||
- Improvements to the `bitops` module, including bitslices, non-mutating versions
|
||||
of the original masking functions, `mask`/`masked`, and varargs support for
|
||||
`bitand`, `bitor`, and `bitxor`.
|
||||
|
||||
- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed
|
||||
into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` for consistency
|
||||
with regular proc definitions (although you cannot use semicolons).
|
||||
|
||||
Pragmas and using a name are now allowed on the lefthand side of `=>`. Here
|
||||
is an example of these changes:
|
||||
```nim
|
||||
import sugar
|
||||
|
||||
foo(x, y: int) {.noSideEffect.} => x + y
|
||||
|
||||
# is transformed into
|
||||
|
||||
proc foo(x: int, y: int): auto {.noSideEffect.} = x + y
|
||||
```
|
||||
|
||||
- The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters.
|
||||
|
||||
- The `times` module now handles the default value for `DateTime` more consistently.
|
||||
Most procs raise an assertion error when given
|
||||
an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`).
|
||||
The proc `times.isInitialized` has been added which can be used to check if
|
||||
a `DateTime` has been initialized.
|
||||
|
||||
- Fix a bug where calling `close` on io streams in `osproc.startProcess` was a noop and led to
|
||||
hangs if a process had both reads from stdin and writes (e.g. to stdout).
|
||||
|
||||
- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
|
||||
- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.
|
||||
|
||||
- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
|
||||
parameters.
|
||||
|
||||
- Added a `ssl_config` module containing lists of secure ciphers as recommended by
|
||||
[Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS)
|
||||
|
||||
- `net.newContext` now defaults to the list of ciphers targeting
|
||||
["Intermediate compatibility"](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29)
|
||||
per Mozilla's recommendation instead of `ALL`. This change should protect
|
||||
users from the use of weak and insecure ciphers while still provides
|
||||
adequate compatibility with the majority of the Internet.
|
||||
|
||||
- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json
|
||||
serialization/deserialization of custom types was added.
|
||||
|
||||
- A new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``
|
||||
was added.
|
||||
- Added `rstgen.rstToLatex` a convenience proc for `renderRstToOut` and `initRstGenerator`.
|
||||
- Added `os.normalizeExe`.
|
||||
- `macros.newLit` now preserves named vs unnamed tuples.
|
||||
- Added `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
|
||||
- Added `typetraits.elementType` to get the element type of an iterable.
|
||||
- `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`;
|
||||
`$tuple[]` is now `"tuple[]"` instead of `"tuple"`;
|
||||
`$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"`
|
||||
- Added `macros.extractDocCommentsAndRunnables` helper.
|
||||
|
||||
- `strformat.fmt` and `strformat.&` support `specifier =`. `fmt"{expr=}"` now
|
||||
expands to `fmt"expr={expr}"`.
|
||||
- Deprecations: instead of `os.existsDir` use `dirExists`, instead of `os.existsFile` use `fileExists`.
|
||||
|
||||
- Added the `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
|
||||
- Made `maxLines` argument `Positive` in `logging.newRollingFileLogger`,
|
||||
because negative values will result in a new file being created for each logged
|
||||
line which doesn't make sense.
|
||||
- Changed `log` in `logging` to use proper log level for JavaScript,
|
||||
e.g. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc.
|
||||
- Tables, HashSets, SharedTables and deques don't require anymore that the passed
|
||||
initial size must be a power of two - this is done internally.
|
||||
Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore.
|
||||
`CountTable.inc` takes `val: int` again not `val: Positive`; i.e. it can "count down" again.
|
||||
- Removed deprecated symbols from `macros` module, some of which were deprecated already in `0.15`.
|
||||
- Removed `sugar.distinctBase`, deprecated since `0.19`. Use `typetraits.distinctBase`.
|
||||
- `asyncdispatch.PDispatcher.handles` is exported so that an external low-level libraries can access it.
|
||||
|
||||
- `std/with`, `sugar.dup` now support object field assignment expressions:
|
||||
```nim
|
||||
import std/with
|
||||
|
||||
type Foo = object
|
||||
x, y: int
|
||||
|
||||
var foo = Foo()
|
||||
with foo:
|
||||
x = 10
|
||||
y = 20
|
||||
|
||||
echo foo
|
||||
```
|
||||
|
||||
- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead
|
||||
cannot be applied to every use case. The limitations and the (lack of) reliability
|
||||
of `round` are well documented.
|
||||
|
||||
- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code
|
||||
from the database that matches the protocol `name`.
|
||||
|
||||
- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc.
|
||||
|
||||
- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals:
|
||||
```nim
|
||||
import strutils
|
||||
echo dedent """
|
||||
This
|
||||
is
|
||||
cool!
|
||||
"""
|
||||
```
|
||||
|
||||
- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
|
||||
|
||||
- Added `readLines(p: Process)` to `osproc`.
|
||||
|
||||
- Added the below `toX` procs for collections. The usage is similar to procs such as
|
||||
`sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the
|
||||
respective empty collection and add items manually.
|
||||
* `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of
|
||||
items or an `openArray` of pairs.
|
||||
* `deques.toDeque`, which creates a `Deque` from an `openArray`.
|
||||
* `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`.
|
||||
* `intsets.toIntSet`, which creates an `IntSet` from an `openArray`.
|
||||
|
||||
- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval
|
||||
at which progress callbacks are called.
|
||||
|
||||
- Added `os.copyFileToDir`.
|
||||
|
||||
## Language changes
|
||||
|
||||
- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
|
||||
`wasMoved` calls where needed.
|
||||
- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there
|
||||
is no need to update your code. This change was backported to 1.2 too so you can use the
|
||||
more readable `=copy` without loss of compatibility.
|
||||
|
||||
- In the newruntime it is now allowed to assign to the discriminator field
|
||||
without restrictions as long as the case object doesn't have a custom destructor.
|
||||
The discriminator value doesn't have to be a constant either. If you have a
|
||||
custom destructor for a case object and you do want to freely assign discriminator
|
||||
fields, it is recommended to refactor the object into 2 objects like this:
|
||||
|
||||
```nim
|
||||
type
|
||||
MyObj = object
|
||||
case kind: bool
|
||||
of true: y: ptr UncheckedArray[float]
|
||||
of false: z: seq[int]
|
||||
|
||||
proc `=destroy`(x: MyObj) =
|
||||
if x.kind and x.y != nil:
|
||||
deallocShared(x.y)
|
||||
```
|
||||
Refactor into:
|
||||
```nim
|
||||
type
|
||||
MySubObj = object
|
||||
val: ptr UncheckedArray[float]
|
||||
MyObj = object
|
||||
case kind: bool
|
||||
of true: y: MySubObj
|
||||
of false: z: seq[int]
|
||||
|
||||
proc `=destroy`(x: MySubObj) =
|
||||
if x.val != nil:
|
||||
deallocShared(x.val)
|
||||
```
|
||||
- `getImpl` on enum type symbols now returns field syms instead of idents. This helps
|
||||
with writing typed macros. The old behavior for backwards compatibility can be restored
|
||||
with `--useVersion:1.0`.
|
||||
- The typed AST for proc headers will now have the arguments be syms instead of idents.
|
||||
This helps with writing typed macros. The old behaviour for backwards compatibility can
|
||||
be restored with `--useVersion:1.0`.
|
||||
- ``let`` statements can now be used without a value if declared with
|
||||
``importc``/``importcpp``/``importjs``/``importobjc``.
|
||||
- The keyword `from` is now usable as an operator.
|
||||
- Exceptions inheriting from `system.Defect` are no longer tracked with
|
||||
the `.raises: []` exception tracking mechanism. This is more consistent with the
|
||||
built-in operations. The following always used to compile (and still does):
|
||||
```nim
|
||||
proc mydiv(a, b): int {.raises: [].} =
|
||||
a div b # can raise an DivByZeroDefect
|
||||
```
|
||||
|
||||
Now also this compiles:
|
||||
```nim
|
||||
proc mydiv(a, b): int {.raises: [].} =
|
||||
if b == 0: raise newException(DivByZeroDefect, "division by zero")
|
||||
else: result = a div b
|
||||
```
|
||||
|
||||
The reason for this is that `DivByZeroDefect` inherits from `Defect` and
|
||||
with `--panics:on` `Defects` become unrecoverable errors.
|
||||
|
||||
- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes.
|
||||
- Deprecated the `{.unroll.}` pragma, because it was always ignored by the compiler anyway.
|
||||
- Removed the deprecated `strutils.isNilOrWhitespace`.
|
||||
- Removed the deprecated `sharedtables.initSharedTable`.
|
||||
- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
|
||||
- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
|
||||
|
||||
- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour.
|
||||
|
||||
- There is a new experimental feature called "strictFuncs" which makes the definition of
|
||||
`.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs)
|
||||
for more information.
|
||||
|
||||
- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer
|
||||
an experimental feature. In other words, you don't have to write pragma
|
||||
`{.experimental: "forLoopMacros".}` if you want to use them.
|
||||
|
||||
- Added the ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased
|
||||
performance this keyword can enable.
|
||||
|
||||
- `items` no longer compiles with enums with holes as its behavior was error prone, see #14004.
|
||||
- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via
|
||||
`--deepcopy:on`.
|
||||
|
||||
- Added the `std/effecttraits` module for introspection of the inferred effects.
|
||||
We hope this enables `async` macros that are precise about the possible exceptions that
|
||||
can be raised.
|
||||
- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be
|
||||
written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new
|
||||
preferred way of writing these, emphasizing their unsafe nature.
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
|
||||
- The `define` and `undef` pragmas have been de-deprecated.
|
||||
- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache`
|
||||
so that the output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to
|
||||
avoid recompilations when sources don't change.
|
||||
Example:
|
||||
```bash
|
||||
nim r compiler/nim.nim --help # only compiled the first time
|
||||
echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too
|
||||
nim r compiler/nim.nim --fullhelp # no recompilation
|
||||
nim r --nimcache:/tmp main # binary saved to /tmp/main
|
||||
```
|
||||
- `--hint:processing` is now supported and means `--hint:processing:on`
|
||||
(likewise with other hints and warnings), which is consistent with all other bool flags.
|
||||
(since 1.3.3).
|
||||
- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`.
|
||||
- Added the new hint `--hint:msgOrigin` will show where a compiler msg (hint|warning|error)
|
||||
was generated; this helps in particular when it's non obvious where it came from
|
||||
either because multiple locations generate the same message, or because the
|
||||
message involves runtime formatting.
|
||||
- Added the new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change the backend; can be
|
||||
used with any command (e.g. nim r, doc, check etc); safe to re-assign.
|
||||
- Added the new flag `--doccmd:cmd` to pass additional flags for runnableExamples,
|
||||
e.g.: `--doccmd:-d:foo --threads`
|
||||
use `--doccmd:skip` to skip runnableExamples and rst test snippets.
|
||||
- Added the new flag `--usenimcache` to output binary files to nimcache.
|
||||
- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override
|
||||
how an example is compiled and run, for example to change the backend.
|
||||
- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified
|
||||
(with or without `--project`); passing `--project` now automatically generates
|
||||
an index and enables search.
|
||||
See [docgen](docgen.html#introduction-quick-start) for details.
|
||||
- Removed the `--oldNewlines` switch.
|
||||
- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings.
|
||||
- Removed the `--oldast` switch.
|
||||
- Removed the `--oldgensym` switch.
|
||||
- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is
|
||||
now "typed" instead of "stmt".
|
||||
- Sink inference is now disabled per default and has to enabled explicitly via
|
||||
`--sinkInference:on`. *Note*: For the standard library sink inference remains
|
||||
enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory
|
||||
management modes.
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- `nimsuggest` now returns both the forward declaration and the
|
||||
implementation location upon a `def` query. Previously the behavior was
|
||||
to return the forward declaration only.
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed "repr() not available for uint{,8,16,32,64} under --gc:arc"
|
||||
([#13872](https://github.com/nim-lang/Nim/issues/13872))
|
||||
- Fixed "Critical: 1 completed Future, multiple await: Only 1 await will be awakened (the last one)"
|
||||
([#13889](https://github.com/nim-lang/Nim/issues/13889))
|
||||
- Fixed "crash on openarray interator with argument in stmtListExpr"
|
||||
([#13739](https://github.com/nim-lang/Nim/issues/13739))
|
||||
- Fixed "Some compilers on Windows don't work"
|
||||
([#13910](https://github.com/nim-lang/Nim/issues/13910))
|
||||
- Fixed "httpclient hangs if it recieves an HTTP 204 (No Content)"
|
||||
([#13894](https://github.com/nim-lang/Nim/issues/13894))
|
||||
- Fixed ""distinct uint64" type corruption on 32-bit, when using {.borrow.} operators"
|
||||
([#13902](https://github.com/nim-lang/Nim/issues/13902))
|
||||
- Fixed "Regression: impossible to use typed pragmas with proc types"
|
||||
([#13909](https://github.com/nim-lang/Nim/issues/13909))
|
||||
- Fixed "openssl wrapper corrupts stack on OpenSSL 1.1.1f + Android"
|
||||
([#13903](https://github.com/nim-lang/Nim/issues/13903))
|
||||
- Fixed "C compile error with --gc:arc on version 1.2.0 "unknown type name 'TGenericSeq'"
|
||||
([#13863](https://github.com/nim-lang/Nim/issues/13863))
|
||||
- Fixed "var return type for proc doesn't work at c++ backend"
|
||||
([#13848](https://github.com/nim-lang/Nim/issues/13848))
|
||||
- Fixed "TimeFormat() should raise an error but craches at compilation time"
|
||||
([#12864](https://github.com/nim-lang/Nim/issues/12864))
|
||||
- Fixed "gc:arc cannot fully support threadpool with FlowVar"
|
||||
([#13781](https://github.com/nim-lang/Nim/issues/13781))
|
||||
- Fixed "simple 'var openarray[char]' assignment crash when the openarray source is a local string and using gc:arc"
|
||||
([#14003](https://github.com/nim-lang/Nim/issues/14003))
|
||||
- Fixed "Cant use expressions with `when` in `type` sections."
|
||||
([#14007](https://github.com/nim-lang/Nim/issues/14007))
|
||||
- Fixed "`for a in MyEnum` gives incorrect results with enum with holes"
|
||||
([#14001](https://github.com/nim-lang/Nim/issues/14001))
|
||||
- Fixed "Trivial crash"
|
||||
([#12741](https://github.com/nim-lang/Nim/issues/12741))
|
||||
- Fixed "Enum with holes cannot be used as Table index"
|
||||
([#12834](https://github.com/nim-lang/Nim/issues/12834))
|
||||
- Fixed "spawn proc that uses typedesc crashes the compiler"
|
||||
([#14014](https://github.com/nim-lang/Nim/issues/14014))
|
||||
- Fixed "Docs Search `Results` box styling is not Dark Mode Friendly"
|
||||
([#13972](https://github.com/nim-lang/Nim/issues/13972))
|
||||
- Fixed "--gc:arc -d:useSysAssert undeclared identifier `cstderr` with newSeq"
|
||||
([#14038](https://github.com/nim-lang/Nim/issues/14038))
|
||||
- Fixed "issues in the manual"
|
||||
([#12486](https://github.com/nim-lang/Nim/issues/12486))
|
||||
- Fixed "Annoying warning: inherit from a more precise exception type like ValueError, IOError or OSError [InheritFromException]"
|
||||
([#14052](https://github.com/nim-lang/Nim/issues/14052))
|
||||
- Fixed "relativePath("foo", "/") and relativePath("/", "foo") is wrong"
|
||||
([#13222](https://github.com/nim-lang/Nim/issues/13222))
|
||||
- Fixed "[regression] `parseEnum` does not work anymore for enums with holes"
|
||||
([#14030](https://github.com/nim-lang/Nim/issues/14030))
|
||||
- Fixed "Exception types in the stdlib should inherit from `CatchableError` or `Defect`, not `Exception`"
|
||||
([#10288](https://github.com/nim-lang/Nim/issues/10288))
|
||||
- Fixed "Make debugSend and debugRecv procs public in smtp.nim"
|
||||
([#12189](https://github.com/nim-lang/Nim/issues/12189))
|
||||
- Fixed "xmltree need add raw text, when add style element"
|
||||
([#14064](https://github.com/nim-lang/Nim/issues/14064))
|
||||
- Fixed "raises requirement does not propagate to derived methods"
|
||||
([#8481](https://github.com/nim-lang/Nim/issues/8481))
|
||||
- Fixed "tests/stdlib/tgetaddrinfo.nim fails on NetBSD"
|
||||
([#14091](https://github.com/nim-lang/Nim/issues/14091))
|
||||
- Fixed "tests/niminaction/Chapter8/sdl/sdl_test.nim fails on NetBSD"
|
||||
([#14088](https://github.com/nim-lang/Nim/issues/14088))
|
||||
- Fixed "Incorrect escape sequence for example in jsffi library documentation"
|
||||
([#14110](https://github.com/nim-lang/Nim/issues/14110))
|
||||
- Fixed "HCR: Can not link exported const, in external library"
|
||||
([#13915](https://github.com/nim-lang/Nim/issues/13915))
|
||||
- Fixed "Cannot import std/unidecode"
|
||||
([#14112](https://github.com/nim-lang/Nim/issues/14112))
|
||||
- Fixed "macOS: dsymutil should not be called on static libraries"
|
||||
([#14132](https://github.com/nim-lang/Nim/issues/14132))
|
||||
- Fixed "nim jsondoc -o:doc.json filename.nim fails when sequences without a type are used"
|
||||
([#14066](https://github.com/nim-lang/Nim/issues/14066))
|
||||
- Fixed "algorithm.sortedByIt template corrupts tuple input under --gc:arc"
|
||||
([#14079](https://github.com/nim-lang/Nim/issues/14079))
|
||||
- Fixed "Invalid C code with lvalue conversion"
|
||||
([#14160](https://github.com/nim-lang/Nim/issues/14160))
|
||||
- Fixed "strformat: doc example fails"
|
||||
([#14054](https://github.com/nim-lang/Nim/issues/14054))
|
||||
- Fixed "Nim doc fail to run for nim 1.2.0 (nim 1.0.4 is ok)"
|
||||
([#13986](https://github.com/nim-lang/Nim/issues/13986))
|
||||
- Fixed "Exception when converting csize to clong"
|
||||
([#13698](https://github.com/nim-lang/Nim/issues/13698))
|
||||
- Fixed "[Documentation] overloading using named arguments works but is not documented"
|
||||
([#11932](https://github.com/nim-lang/Nim/issues/11932))
|
||||
- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
|
||||
([#14142](https://github.com/nim-lang/Nim/issues/14142))
|
||||
- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
|
||||
([#14142](https://github.com/nim-lang/Nim/issues/14142))
|
||||
- Fixed "runnableExamples doc gen crashes compiler with `except Exception as e` syntax"
|
||||
([#14177](https://github.com/nim-lang/Nim/issues/14177))
|
||||
- Fixed "[ARC] Segfault with cyclic references (?)"
|
||||
([#14159](https://github.com/nim-lang/Nim/issues/14159))
|
||||
- Fixed "Semcheck regression when accessing a static parameter in proc"
|
||||
([#14136](https://github.com/nim-lang/Nim/issues/14136))
|
||||
- Fixed "iterator walkDir doesn't work with -d:useWinAnsi"
|
||||
([#14201](https://github.com/nim-lang/Nim/issues/14201))
|
||||
- Fixed "cas is wrong for tcc"
|
||||
([#14151](https://github.com/nim-lang/Nim/issues/14151))
|
||||
- Fixed "proc execCmdEx doesn't work with -d:useWinAnsi"
|
||||
([#14203](https://github.com/nim-lang/Nim/issues/14203))
|
||||
- Fixed "Use -d:nimEmulateOverflowChecks by default?"
|
||||
([#14209](https://github.com/nim-lang/Nim/issues/14209))
|
||||
- Fixed "Old sequences with destructor objects bug"
|
||||
([#14217](https://github.com/nim-lang/Nim/issues/14217))
|
||||
- Fixed "[ARC] ICE when changing the discriminant of a return value"
|
||||
([#14244](https://github.com/nim-lang/Nim/issues/14244))
|
||||
- Fixed "[ARC] ICE with static objects"
|
||||
([#14236](https://github.com/nim-lang/Nim/issues/14236))
|
||||
- Fixed "[ARC] "internal error: environment misses: a" in a finalizer"
|
||||
([#14243](https://github.com/nim-lang/Nim/issues/14243))
|
||||
- Fixed "[ARC] compile failure using repr with object containing `ref seq[string]`"
|
||||
([#14270](https://github.com/nim-lang/Nim/issues/14270))
|
||||
- Fixed "[ARC] implicit move on last use happening on non-last use"
|
||||
([#14269](https://github.com/nim-lang/Nim/issues/14269))
|
||||
- Fixed "[ARC] Compiler crash with a recursive non-ref object variant"
|
||||
([#14294](https://github.com/nim-lang/Nim/issues/14294))
|
||||
- Fixed "htmlparser.parseHtml behaves differently using --gc:arc or --gc:orc"
|
||||
([#13946](https://github.com/nim-lang/Nim/issues/13946))
|
||||
- Fixed "Invalid return value of openProcess is NULL rather than INVALID_HANDLE_VALUE(-1) in windows"
|
||||
([#14289](https://github.com/nim-lang/Nim/issues/14289))
|
||||
- Fixed "ARC codegen bug with inline iterators"
|
||||
([#14219](https://github.com/nim-lang/Nim/issues/14219))
|
||||
- Fixed "Building koch on OpenBSD fails unless the Nim directory is in `$PATH`"
|
||||
([#13758](https://github.com/nim-lang/Nim/issues/13758))
|
||||
- Fixed "[gc:arc] case object assignment SIGSEGV: destroy not called for primitive type "
|
||||
([#14312](https://github.com/nim-lang/Nim/issues/14312))
|
||||
- Fixed "Crash when using thread and --gc:arc "
|
||||
([#13881](https://github.com/nim-lang/Nim/issues/13881))
|
||||
- Fixed "Getting "Warning: Cannot prove that 'result' is initialized" for an importcpp'd proc with `var T` return type"
|
||||
([#14314](https://github.com/nim-lang/Nim/issues/14314))
|
||||
- Fixed "`nim cpp -r --gc:arc` segfaults on caught AssertionError"
|
||||
([#13071](https://github.com/nim-lang/Nim/issues/13071))
|
||||
- Fixed "tests/async/tasyncawait.nim is recently very flaky"
|
||||
([#14320](https://github.com/nim-lang/Nim/issues/14320))
|
||||
- Fixed "Documentation nonexistent quitprocs module"
|
||||
([#14331](https://github.com/nim-lang/Nim/issues/14331))
|
||||
- Fixed "SIGSEV encountered when creating threads in a loop w/ --gc:arc"
|
||||
([#13935](https://github.com/nim-lang/Nim/issues/13935))
|
||||
- Fixed "nim-gdb is missing from all released packages"
|
||||
([#13104](https://github.com/nim-lang/Nim/issues/13104))
|
||||
- Fixed "sysAssert error with gc:arc on 3 line program"
|
||||
([#13862](https://github.com/nim-lang/Nim/issues/13862))
|
||||
- Fixed "compiler error with inline async proc and pragma"
|
||||
([#13998](https://github.com/nim-lang/Nim/issues/13998))
|
||||
- Fixed "[ARC] Compiler crash when adding to a seq[ref Object]"
|
||||
([#14333](https://github.com/nim-lang/Nim/issues/14333))
|
||||
- Fixed "nimvm: sysFatal: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldError]"
|
||||
([#14340](https://github.com/nim-lang/Nim/issues/14340))
|
||||
- Fixed "[Regression] karax events are not firing "
|
||||
([#14350](https://github.com/nim-lang/Nim/issues/14350))
|
||||
- Fixed "odbcsql module has some wrong integer types"
|
||||
([#9771](https://github.com/nim-lang/Nim/issues/9771))
|
||||
- Fixed "db_sqlite needs sqlPrepared"
|
||||
([#13559](https://github.com/nim-lang/Nim/issues/13559))
|
||||
- Fixed "[Regression] `createThread` is not GC-safe"
|
||||
([#14370](https://github.com/nim-lang/Nim/issues/14370))
|
||||
- Fixed "Broken example on hot code reloading"
|
||||
([#14380](https://github.com/nim-lang/Nim/issues/14380))
|
||||
- Fixed "runnableExamples block with `except` on specified error fails with `nim doc`"
|
||||
([#12746](https://github.com/nim-lang/Nim/issues/12746))
|
||||
- Fixed "compiler as a library: findNimStdLibCompileTime fails to find system.nim"
|
||||
([#12293](https://github.com/nim-lang/Nim/issues/12293))
|
||||
- Fixed "5 bugs with importcpp exceptions"
|
||||
([#14369](https://github.com/nim-lang/Nim/issues/14369))
|
||||
- Fixed "Docs shouldn't collapse pragmas inside runnableExamples/code blocks"
|
||||
([#14174](https://github.com/nim-lang/Nim/issues/14174))
|
||||
- Fixed "Bad codegen/emit for hashes.hiXorLo in some contexts."
|
||||
([#14394](https://github.com/nim-lang/Nim/issues/14394))
|
||||
- Fixed "Boehm GC does not scan thread-local storage"
|
||||
([#14364](https://github.com/nim-lang/Nim/issues/14364))
|
||||
- Fixed "RVO not exception safe"
|
||||
([#14126](https://github.com/nim-lang/Nim/issues/14126))
|
||||
- Fixed "runnableExamples that are only compiled"
|
||||
([#10731](https://github.com/nim-lang/Nim/issues/10731))
|
||||
- Fixed "`foldr` raises IndexError when called on sequence"
|
||||
([#14404](https://github.com/nim-lang/Nim/issues/14404))
|
||||
- Fixed "moveFile does not overwrite destination file"
|
||||
([#14057](https://github.com/nim-lang/Nim/issues/14057))
|
||||
- Fixed "doc2 outputs in current work dir"
|
||||
([#6583](https://github.com/nim-lang/Nim/issues/6583))
|
||||
- Fixed "[docgen] proc doc comments silently omitted after 1st runnableExamples"
|
||||
([#9227](https://github.com/nim-lang/Nim/issues/9227))
|
||||
- Fixed "`nim doc --project` shows '@@/' instead of '../' for relative paths to submodules"
|
||||
([#14448](https://github.com/nim-lang/Nim/issues/14448))
|
||||
- Fixed "re, nre have wrong `start` semantics"
|
||||
([#14284](https://github.com/nim-lang/Nim/issues/14284))
|
||||
- Fixed "runnableExamples should preserve source code doc comments, strings, and (maybe) formatting"
|
||||
([#8871](https://github.com/nim-lang/Nim/issues/8871))
|
||||
- Fixed "`nim doc ..` fails when runnableExamples uses `$` [devel] [regression]"
|
||||
([#14485](https://github.com/nim-lang/Nim/issues/14485))
|
||||
- Fixed "`items` is 20%~30% slower than iteration via an index"
|
||||
([#14421](https://github.com/nim-lang/Nim/issues/14421))
|
||||
- Fixed "ARC: unreliable setLen "
|
||||
([#14495](https://github.com/nim-lang/Nim/issues/14495))
|
||||
- Fixed "lent is unsafe: after #14447 you can modify variables with "items" loop for sequences"
|
||||
([#14498](https://github.com/nim-lang/Nim/issues/14498))
|
||||
- Fixed "`var op = fn()` wrongly gives warning `ObservableStores` with `object of RootObj` type"
|
||||
([#14514](https://github.com/nim-lang/Nim/issues/14514))
|
||||
- Fixed "Compiler assertion"
|
||||
([#14562](https://github.com/nim-lang/Nim/issues/14562))
|
||||
- Fixed "Can't get `ord` of a value of a Range type in the JS backend "
|
||||
([#14570](https://github.com/nim-lang/Nim/issues/14570))
|
||||
- Fixed "js: can't take addr of param (including implicitly via `lent`)"
|
||||
([#14576](https://github.com/nim-lang/Nim/issues/14576))
|
||||
- Fixed "{.noinit.} ignored in for loop -> bad codegen for non-movable types"
|
||||
([#14118](https://github.com/nim-lang/Nim/issues/14118))
|
||||
- Fixed "generic destructor gives: `Error: unresolved generic parameter`"
|
||||
([#14315](https://github.com/nim-lang/Nim/issues/14315))
|
||||
- Fixed "Memory leak with arc gc"
|
||||
([#14568](https://github.com/nim-lang/Nim/issues/14568))
|
||||
- Fixed "escape analysis broken with `lent`"
|
||||
([#14557](https://github.com/nim-lang/Nim/issues/14557))
|
||||
- Fixed "`wrapWords` seems to ignore linebreaks when wrapping, leaving breaks in the wrong place"
|
||||
([#14579](https://github.com/nim-lang/Nim/issues/14579))
|
||||
- Fixed "`lent` gives wrong results with -d:release"
|
||||
([#14578](https://github.com/nim-lang/Nim/issues/14578))
|
||||
- Fixed "Nested await expressions regression: `await a(await expandValue())` doesnt compile"
|
||||
([#14279](https://github.com/nim-lang/Nim/issues/14279))
|
||||
- Fixed "windows CI docs fails with strange errors"
|
||||
([#14545](https://github.com/nim-lang/Nim/issues/14545))
|
||||
- Fixed "[CI] tests/async/tioselectors.nim flaky test for freebsd + OSX CI"
|
||||
([#13166](https://github.com/nim-lang/Nim/issues/13166))
|
||||
- Fixed "seq.setLen sometimes doesn't zero memory"
|
||||
([#14655](https://github.com/nim-lang/Nim/issues/14655))
|
||||
- Fixed "`nim dump` is roughly 100x slower in 1.3 versus 1.2"
|
||||
([#14179](https://github.com/nim-lang/Nim/issues/14179))
|
||||
- Fixed "Regression: devel docgen cannot generate document for method"
|
||||
([#14691](https://github.com/nim-lang/Nim/issues/14691))
|
||||
- Fixed "recently flaky tests/async/t7758.nim"
|
||||
([#14685](https://github.com/nim-lang/Nim/issues/14685))
|
||||
- Fixed "Bind no longer working in generic procs."
|
||||
([#11811](https://github.com/nim-lang/Nim/issues/11811))
|
||||
- Fixed "The pegs module doesn't work with generics!"
|
||||
([#14718](https://github.com/nim-lang/Nim/issues/14718))
|
||||
- Fixed "Defer is not properly working for asynchronous procedures."
|
||||
([#13899](https://github.com/nim-lang/Nim/issues/13899))
|
||||
- Fixed "Add an ARC test with threads in a loop"
|
||||
([#14690](https://github.com/nim-lang/Nim/issues/14690))
|
||||
- Fixed "[goto exceptions] {.noReturn.} pragma is not detected in a case expression"
|
||||
([#14458](https://github.com/nim-lang/Nim/issues/14458))
|
||||
- Fixed "[exceptions:goto] C compiler error with dynlib pragma calling a proc"
|
||||
([#14240](https://github.com/nim-lang/Nim/issues/14240))
|
||||
- Fixed "Cannot borrow var float64 in infix assignment"
|
||||
([#14440](https://github.com/nim-lang/Nim/issues/14440))
|
||||
- Fixed "lib/pure/memfiles.nim: compilation error with --taintMode:on"
|
||||
([#14760](https://github.com/nim-lang/Nim/issues/14760))
|
||||
- Fixed "newWideCString allocates a multiple of the memory needed"
|
||||
([#14750](https://github.com/nim-lang/Nim/issues/14750))
|
||||
- Fixed "Nim source archive install: 'install.sh' fails with error: cp: cannot stat 'bin/nim-gdb': No such file or directory"
|
||||
([#14748](https://github.com/nim-lang/Nim/issues/14748))
|
||||
- Fixed "`nim cpp -r tests/exception/t9657` hangs"
|
||||
([#10343](https://github.com/nim-lang/Nim/issues/10343))
|
||||
- Fixed "Detect tool fails on FreeBSD"
|
||||
([#14715](https://github.com/nim-lang/Nim/issues/14715))
|
||||
- Fixed "compiler crash: `findUnresolvedStatic` "
|
||||
([#14802](https://github.com/nim-lang/Nim/issues/14802))
|
||||
- Fixed "seq namespace (?) regression"
|
||||
([#4796](https://github.com/nim-lang/Nim/issues/4796))
|
||||
- Fixed "Possible out of bounds string access in std/colors parseColor and isColor"
|
||||
([#14839](https://github.com/nim-lang/Nim/issues/14839))
|
||||
- Fixed "compile error on latest devel with orc and ssl"
|
||||
([#14647](https://github.com/nim-lang/Nim/issues/14647))
|
||||
- Fixed "[minor] `$` wrong for type tuple"
|
||||
([#13432](https://github.com/nim-lang/Nim/issues/13432))
|
||||
- Fixed "Documentation missing on devel asyncftpclient"
|
||||
([#14846](https://github.com/nim-lang/Nim/issues/14846))
|
||||
- Fixed "nimpretty is confused with a trailing comma in enum definition"
|
||||
([#14401](https://github.com/nim-lang/Nim/issues/14401))
|
||||
- Fixed "Output arguments get ignored when compiling with --app:staticlib"
|
||||
([#12745](https://github.com/nim-lang/Nim/issues/12745))
|
||||
- Fixed "[ARC] destructive move destroys the object too early"
|
||||
([#14396](https://github.com/nim-lang/Nim/issues/14396))
|
||||
- Fixed "highlite.getNextToken() crashes if the buffer string is "echo "\"""
|
||||
([#14830](https://github.com/nim-lang/Nim/issues/14830))
|
||||
- Fixed "Memory corruption with --gc:arc with a seq of objects with an empty body."
|
||||
([#14472](https://github.com/nim-lang/Nim/issues/14472))
|
||||
- Fixed "Stropped identifiers don't work as field names in tuple literals"
|
||||
([#14911](https://github.com/nim-lang/Nim/issues/14911))
|
||||
- Fixed "Please revert my commit"
|
||||
([#14930](https://github.com/nim-lang/Nim/issues/14930))
|
||||
- Fixed "[ARC] C compiler error with inline iterators and imports"
|
||||
([#14864](https://github.com/nim-lang/Nim/issues/14864))
|
||||
- Fixed "AsyncHttpClient segfaults with gc:orc, possibly memory corruption"
|
||||
([#14402](https://github.com/nim-lang/Nim/issues/14402))
|
||||
- Fixed "[ARC] Template with a block evaluating to a GC'd value results in a compiler crash"
|
||||
([#14899](https://github.com/nim-lang/Nim/issues/14899))
|
||||
- Fixed "[ARC] Weird issue with if expressions and templates"
|
||||
([#14900](https://github.com/nim-lang/Nim/issues/14900))
|
||||
- Fixed "xmlparser does not compile on devel"
|
||||
([#14805](https://github.com/nim-lang/Nim/issues/14805))
|
||||
- Fixed "returning lent T from a var T param gives codegen errors or SIGSEGV"
|
||||
([#14878](https://github.com/nim-lang/Nim/issues/14878))
|
||||
- Fixed "[ARC] Weird issue with if expressions and templates"
|
||||
([#14900](https://github.com/nim-lang/Nim/issues/14900))
|
||||
- Fixed "threads:on + gc:orc + unittest = C compiler errors"
|
||||
([#14865](https://github.com/nim-lang/Nim/issues/14865))
|
||||
- Fixed "mitems, mpairs doesn't work at compile time anymore"
|
||||
([#12129](https://github.com/nim-lang/Nim/issues/12129))
|
||||
- Fixed "strange result from executing code in const expression"
|
||||
([#10465](https://github.com/nim-lang/Nim/issues/10465))
|
||||
- Fixed "Same warning printed 3 times"
|
||||
([#11009](https://github.com/nim-lang/Nim/issues/11009))
|
||||
- Fixed "type alias for generic typeclass doesn't work"
|
||||
([#4668](https://github.com/nim-lang/Nim/issues/4668))
|
||||
- Fixed "exceptions:goto Bug devel codegen lvalue NIM_FALSE=NIM_FALSE"
|
||||
([#14925](https://github.com/nim-lang/Nim/issues/14925))
|
||||
- Fixed "the --useVersion:1.0 no longer works in devel"
|
||||
([#14912](https://github.com/nim-lang/Nim/issues/14912))
|
||||
- Fixed "template declaration of iterator doesn't compile"
|
||||
([#4722](https://github.com/nim-lang/Nim/issues/4722))
|
||||
- Fixed "Compiler crash on type inheritance with static generic parameter and equality check"
|
||||
([#12571](https://github.com/nim-lang/Nim/issues/12571))
|
||||
- Fixed "Nim crashes while handling a cast in async circumstances."
|
||||
([#13815](https://github.com/nim-lang/Nim/issues/13815))
|
||||
- Fixed "[ARC] Internal compiler error when calling an iterator from an inline proc "
|
||||
([#14383](https://github.com/nim-lang/Nim/issues/14383))
|
||||
- Fixed ""Cannot instantiate" error when template uses generic type"
|
||||
([#5926](https://github.com/nim-lang/Nim/issues/5926))
|
||||
- Fixed "Different raises behaviour for newTerminal between Linux and Windows"
|
||||
([#12759](https://github.com/nim-lang/Nim/issues/12759))
|
||||
- Fixed "Expand on a type (that defines a proc type) in error message "
|
||||
([#6608](https://github.com/nim-lang/Nim/issues/6608))
|
||||
- Fixed "unittest require quits program with an exit code of 0"
|
||||
([#14475](https://github.com/nim-lang/Nim/issues/14475))
|
||||
- Fixed "Range type: Generics vs concrete type, semcheck difference."
|
||||
([#8426](https://github.com/nim-lang/Nim/issues/8426))
|
||||
- Fixed "[Macro] Type mismatch when parameter name is the same as a field"
|
||||
([#13253](https://github.com/nim-lang/Nim/issues/13253))
|
||||
- Fixed "Generic instantiation failure when converting a sequence of circular generic types to strings"
|
||||
([#10396](https://github.com/nim-lang/Nim/issues/10396))
|
||||
- Fixed "initOptParser ignores argument after value option with empty value."
|
||||
([#13086](https://github.com/nim-lang/Nim/issues/13086))
|
||||
- Fixed "[ARC] proc with both explicit and implicit return results in a C compiler error"
|
||||
([#14985](https://github.com/nim-lang/Nim/issues/14985))
|
||||
- Fixed "Alias type forgets implicit generic params depending on order"
|
||||
([#14990](https://github.com/nim-lang/Nim/issues/14990))
|
||||
- Fixed "[ARC] sequtils.insert has different behaviour between ARC/refc"
|
||||
([#14994](https://github.com/nim-lang/Nim/issues/14994))
|
||||
- Fixed "The documentation for "hot code reloading" references a non-existent npm package"
|
||||
([#13621](https://github.com/nim-lang/Nim/issues/13621))
|
||||
- Fixed "existsDir deprecated but breaking `dir` undeclared"
|
||||
([#15006](https://github.com/nim-lang/Nim/issues/15006))
|
||||
- Fixed "uri.decodeUrl crashes on incorrectly formatted input"
|
||||
([#14082](https://github.com/nim-lang/Nim/issues/14082))
|
||||
- Fixed "testament incorrectly reports time for tests, leading to wrong conclusions"
|
||||
([#14822](https://github.com/nim-lang/Nim/issues/14822))
|
||||
- Fixed "Calling peekChar with Stream returned from osproc.outputStream generate runtime error"
|
||||
([#14906](https://github.com/nim-lang/Nim/issues/14906))
|
||||
- Fixed "localPassC pragma should come *after* other flags"
|
||||
([#14194](https://github.com/nim-lang/Nim/issues/14194))
|
||||
- Fixed ""Could not load" dynamic library at runtime because of hidden dependency"
|
||||
([#2408](https://github.com/nim-lang/Nim/issues/2408))
|
||||
- Fixed "--gc:arc generate invalid code for {.global.} (`«nimErr_» in NIM_UNLIKELY`)"
|
||||
([#14480](https://github.com/nim-lang/Nim/issues/14480))
|
||||
- Fixed "Using `^` from stdlib/math along with converters gives a match for types that aren't SomeNumber"
|
||||
([#15033](https://github.com/nim-lang/Nim/issues/15033))
|
||||
- Fixed "[ARC] Weird exception behaviour from doAssertRaises"
|
||||
([#15026](https://github.com/nim-lang/Nim/issues/15026))
|
||||
- Fixed "[ARC] Compiler crash declaring a finalizer proc directly in 'new'"
|
||||
([#15044](https://github.com/nim-lang/Nim/issues/15044))
|
||||
- Fixed "[ARC] C compiler error when creating a var of a const seq"
|
||||
([#15036](https://github.com/nim-lang/Nim/issues/15036))
|
||||
- Fixed "code with named arguments in proc of winim/com can not been compiled"
|
||||
([#15056](https://github.com/nim-lang/Nim/issues/15056))
|
||||
- Fixed "javascript backend produces javascript code with syntax error in object syntax"
|
||||
([#14534](https://github.com/nim-lang/Nim/issues/14534))
|
||||
- Fixed "--gc:arc should be ignored in JS mode."
|
||||
([#14684](https://github.com/nim-lang/Nim/issues/14684))
|
||||
- Fixed "arc: C compilation error with imported global code using a closure iterator"
|
||||
([#12990](https://github.com/nim-lang/Nim/issues/12990))
|
||||
- Fixed "[ARC] Crash when modifying a string with mitems iterator"
|
||||
([#15052](https://github.com/nim-lang/Nim/issues/15052))
|
||||
- Fixed "[ARC] SIGSEGV when calling a closure as a tuple field in a seq"
|
||||
([#15038](https://github.com/nim-lang/Nim/issues/15038))
|
||||
- Fixed "pass varargs[seq[T]] to iterator give empty seq "
|
||||
([#12576](https://github.com/nim-lang/Nim/issues/12576))
|
||||
- Fixed "Compiler crashes when using string as object variant selector with else branch"
|
||||
([#14189](https://github.com/nim-lang/Nim/issues/14189))
|
||||
- Fixed "JS compiler error related to implicit return and return var type"
|
||||
([#11354](https://github.com/nim-lang/Nim/issues/11354))
|
||||
- Fixed "`nkRecWhen` causes internalAssert in semConstructFields"
|
||||
([#14698](https://github.com/nim-lang/Nim/issues/14698))
|
||||
- Fixed "Memory leaks with async (closure iterators?) under ORC"
|
||||
([#15076](https://github.com/nim-lang/Nim/issues/15076))
|
||||
- Fixed "strutil.insertSep() fails on negative numbers"
|
||||
([#11352](https://github.com/nim-lang/Nim/issues/11352))
|
||||
- Fixed "Constructing a uint64 range on a 32-bit machine leads to incorrect codegen"
|
||||
([#14616](https://github.com/nim-lang/Nim/issues/14616))
|
||||
- Fixed "heapqueue pushpop() proc doesn't compile"
|
||||
([#14139](https://github.com/nim-lang/Nim/issues/14139))
|
||||
- Fixed "[ARC] SIGSEGV when trying to swap in a literal/const string"
|
||||
([#15112](https://github.com/nim-lang/Nim/issues/15112))
|
||||
- Fixed "Defer and --gc:arc"
|
||||
([#15071](https://github.com/nim-lang/Nim/issues/15071))
|
||||
- Fixed "internal error: compiler/semobjconstr.nim(324, 20) example"
|
||||
([#15111](https://github.com/nim-lang/Nim/issues/15111))
|
||||
- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
|
||||
([#15122](https://github.com/nim-lang/Nim/issues/15122))
|
||||
- Fixed "[ARC] SIGSEGV with tuple assignment caused by cursor inference"
|
||||
([#15130](https://github.com/nim-lang/Nim/issues/15130))
|
||||
- Fixed "Issue with --gc:arc at compile time"
|
||||
([#15129](https://github.com/nim-lang/Nim/issues/15129))
|
||||
- Fixed "Writing an empty string to an AsyncFile raises an IndexDefect"
|
||||
([#15148](https://github.com/nim-lang/Nim/issues/15148))
|
||||
- Fixed "Compiler is confused about call convention of function with nested closure"
|
||||
([#5688](https://github.com/nim-lang/Nim/issues/5688))
|
||||
- Fixed "Nil check on each field fails in generic function"
|
||||
([#15101](https://github.com/nim-lang/Nim/issues/15101))
|
||||
- Fixed "{.nimcall.} convention won't avoid the creation of closures"
|
||||
([#8473](https://github.com/nim-lang/Nim/issues/8473))
|
||||
- Fixed "smtp.nim(161, 40) Error: type mismatch: got <typeof(nil)> but expected 'SslContext = void'"
|
||||
([#15177](https://github.com/nim-lang/Nim/issues/15177))
|
||||
- Fixed "[strscans] scanf doesn't match a single character with $+ if it's the end of the string"
|
||||
([#15064](https://github.com/nim-lang/Nim/issues/15064))
|
||||
- Fixed "Crash and incorrect return values when using readPasswordFromStdin on Windows."
|
||||
([#15207](https://github.com/nim-lang/Nim/issues/15207))
|
||||
- Fixed "Possible capture error with fieldPairs and genericParams"
|
||||
([#15221](https://github.com/nim-lang/Nim/issues/15221))
|
||||
- Fixed "The StmtList processing of template parameters can lead to unexpected errors"
|
||||
([#5691](https://github.com/nim-lang/Nim/issues/5691))
|
||||
- Fixed "[ARC] C compiler error when passing a var openArray to a sink openArray"
|
||||
([#15035](https://github.com/nim-lang/Nim/issues/15035))
|
||||
- Fixed "Inconsistent unsigned -> signed RangeDefect usage across integer sizes"
|
||||
([#15210](https://github.com/nim-lang/Nim/issues/15210))
|
||||
- Fixed "toHex results in RangeDefect exception when used with large uint64"
|
||||
([#15257](https://github.com/nim-lang/Nim/issues/15257))
|
||||
- Fixed "Arc sink arg crash"
|
||||
([#15238](https://github.com/nim-lang/Nim/issues/15238))
|
||||
- Fixed "SQL escape in db_mysql is not enough"
|
||||
([#15219](https://github.com/nim-lang/Nim/issues/15219))
|
||||
- Fixed "Mixing 'return' with expressions is allowed in 1.2"
|
||||
([#15280](https://github.com/nim-lang/Nim/issues/15280))
|
||||
- Fixed "os.getFileInfo() causes ICE with --gc:arc on Windows"
|
||||
([#15286](https://github.com/nim-lang/Nim/issues/15286))
|
||||
- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
|
||||
([#15122](https://github.com/nim-lang/Nim/issues/15122))
|
||||
- Fixed "Documentation regression jsre module missing"
|
||||
([#15183](https://github.com/nim-lang/Nim/issues/15183))
|
||||
- Fixed "CountTable.smallest/largest() on empty table either asserts or gives bogus answer"
|
||||
([#15021](https://github.com/nim-lang/Nim/issues/15021))
|
||||
- Fixed "[Regression] Parser regression"
|
||||
([#15305](https://github.com/nim-lang/Nim/issues/15305))
|
||||
- Fixed "[ARC] SIGSEGV with tuple unpacking caused by cursor inference"
|
||||
([#15147](https://github.com/nim-lang/Nim/issues/15147))
|
||||
- Fixed "LwIP/FreeRTOS compile error - missing SIGPIPE and more "
|
||||
([#15302](https://github.com/nim-lang/Nim/issues/15302))
|
||||
- Fixed "Memory leaks with async (closure iterators?) under ORC"
|
||||
([#15076](https://github.com/nim-lang/Nim/issues/15076))
|
||||
- Fixed "Bug compiling with --gc:arg or --gc:orc"
|
||||
([#15325](https://github.com/nim-lang/Nim/issues/15325))
|
||||
- Fixed "memory corruption in tmarshall.nim"
|
||||
([#9754](https://github.com/nim-lang/Nim/issues/9754))
|
||||
- Fixed "typed macros break generic proc definitions"
|
||||
([#15326](https://github.com/nim-lang/Nim/issues/15326))
|
||||
- Fixed "nim doc2 ignores --docSeeSrcUrl parameter"
|
||||
([#6071](https://github.com/nim-lang/Nim/issues/6071))
|
||||
- Fixed "The decodeData Iterator from cgi module crash"
|
||||
([#15369](https://github.com/nim-lang/Nim/issues/15369))
|
||||
- Fixed "|| iterator generates invalid code when compiling with --debugger:native"
|
||||
([#9710](https://github.com/nim-lang/Nim/issues/9710))
|
||||
- Fixed "Wrong number of variables"
|
||||
([#15360](https://github.com/nim-lang/Nim/issues/15360))
|
||||
- Fixed "Coercions with distinct types should traverse pointer modifiers transparently."
|
||||
([#7165](https://github.com/nim-lang/Nim/issues/7165))
|
||||
- Fixed "Error with distinct generic TableRef"
|
||||
([#6060](https://github.com/nim-lang/Nim/issues/6060))
|
||||
- Fixed "Support images in nim docgen"
|
||||
([#6430](https://github.com/nim-lang/Nim/issues/6430))
|
||||
- Fixed "Regression. Double sem check for procs."
|
||||
([#15389](https://github.com/nim-lang/Nim/issues/15389))
|
||||
- Fixed "uri.nim url with literal ipv6 address is printed wrong, and cannot parsed again"
|
||||
([#15333](https://github.com/nim-lang/Nim/issues/15333))
|
||||
- Fixed "[ARC] Object variant gets corrupted with cursor inference"
|
||||
([#15361](https://github.com/nim-lang/Nim/issues/15361))
|
||||
- Fixed "`nim doc ..` compiler crash (regression 0.19.6 => 1.0)"
|
||||
([#14474](https://github.com/nim-lang/Nim/issues/14474))
|
||||
- Fixed "cannot borrow result; what it borrows from is potentially mutated"
|
||||
([#15403](https://github.com/nim-lang/Nim/issues/15403))
|
||||
- Fixed "memory corruption for seq.add(seq) with gc:arc and d:useMalloc "
|
||||
([#14983](https://github.com/nim-lang/Nim/issues/14983))
|
||||
- Fixed "DocGen HTML output appears improperly when encountering text immediately after/before inline monospace; in some cases won't compile"
|
||||
([#11537](https://github.com/nim-lang/Nim/issues/11537))
|
||||
- Fixed "Deepcopy in arc crashes"
|
||||
([#15405](https://github.com/nim-lang/Nim/issues/15405))
|
||||
- Fixed "pop pragma takes invalid input"
|
||||
([#15430](https://github.com/nim-lang/Nim/issues/15430))
|
||||
- Fixed "tests/stdlib/tgetprotobyname fails on NetBSD"
|
||||
([#15452](https://github.com/nim-lang/Nim/issues/15452))
|
||||
- Fixed "defer doesnt work with block, break and await"
|
||||
([#15243](https://github.com/nim-lang/Nim/issues/15243))
|
||||
- Fixed "tests/stdlib/tssl failing on NetBSD"
|
||||
([#15493](https://github.com/nim-lang/Nim/issues/15493))
|
||||
- Fixed "strictFuncs doesn't seem to catch simple ref mutation"
|
||||
([#15508](https://github.com/nim-lang/Nim/issues/15508))
|
||||
- Fixed "Sizeof of case object is incorrect. Showstopper"
|
||||
([#15516](https://github.com/nim-lang/Nim/issues/15516))
|
||||
- Fixed "[ARC] Internal error when trying to use a parallel for loop"
|
||||
([#15512](https://github.com/nim-lang/Nim/issues/15512))
|
||||
- Fixed "[ARC] Type-bound assign op is not being generated"
|
||||
([#15510](https://github.com/nim-lang/Nim/issues/15510))
|
||||
- Fixed "[ARC] Crash when adding openArray proc argument to a local seq"
|
||||
([#15511](https://github.com/nim-lang/Nim/issues/15511))
|
||||
- Fixed "VM: const case object gets some fields zeroed out at runtime"
|
||||
([#13081](https://github.com/nim-lang/Nim/issues/13081))
|
||||
- Fixed "regression(1.2.6 => devel): VM: const case object field access gives: 'sons' is not accessible"
|
||||
([#15532](https://github.com/nim-lang/Nim/issues/15532))
|
||||
- Fixed "Csources: huge size increase (x2.3) in 0.20"
|
||||
([#12027](https://github.com/nim-lang/Nim/issues/12027))
|
||||
- Fixed "Out of date error message for GC options"
|
||||
([#15547](https://github.com/nim-lang/Nim/issues/15547))
|
||||
- Fixed "dbQuote additional escape regression"
|
||||
([#15560](https://github.com/nim-lang/Nim/issues/15560))
|
||||
@@ -1,957 +0,0 @@
|
||||
# v1.6.0 - 2021-10-14
|
||||
|
||||
|
||||
|
||||
# Major new features
|
||||
|
||||
With so many new features, pinpointing the most salient ones is a subjective exercise,
|
||||
but here are a select few:
|
||||
|
||||
|
||||
## `iterable[T]`
|
||||
|
||||
The `iterable[T]` type class was added to match called iterators,
|
||||
which solves a number of long-standing issues related to iterators.
|
||||
Example:
|
||||
|
||||
```nim
|
||||
iterator iota(n: int): int =
|
||||
for i in 0..<n: yield i
|
||||
|
||||
# previously, you'd need `untyped`, which caused other problems such as lack
|
||||
# of type inference, overloading issues, and MCS.
|
||||
template sumOld(a: untyped): untyped = # no type inference possible
|
||||
var result: typeof(block:(for ai in a: ai))
|
||||
for ai in a: result += ai
|
||||
result
|
||||
|
||||
assert sumOld(iota(3)) == 0 + 1 + 2
|
||||
|
||||
# now, you can write:
|
||||
template sum[T](a: iterable[T]): T =
|
||||
# `template sum(a: iterable): auto =` would also be possible
|
||||
var result: T
|
||||
for ai in a: result += ai
|
||||
result
|
||||
|
||||
assert sum(iota(3)) == 0 + 1 + 2 # or `iota(3).sum`
|
||||
```
|
||||
|
||||
In particular iterable arguments can now be used with the method call syntax. For example:
|
||||
```nim
|
||||
import std/[sequtils, os]
|
||||
echo walkFiles("*").toSeq # now works
|
||||
```
|
||||
See PR [#17196](https://github.com/nim-lang/Nim/pull/17196) for additional details.
|
||||
|
||||
|
||||
## Strict effects
|
||||
|
||||
The effect system was refined and there is a new `.effectsOf` annotation that does
|
||||
explicitly what was previously done implicitly. See the
|
||||
[manual](https://nim-lang.github.io/Nim/manual.html#effect-system-effectsof-annotation)
|
||||
for more details.
|
||||
To write code that is portable with older Nim versions, use this idiom:
|
||||
|
||||
```nim
|
||||
when defined(nimHasEffectsOf):
|
||||
{.experimental: "strictEffects".}
|
||||
else:
|
||||
{.pragma: effectsOf.}
|
||||
|
||||
proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.}
|
||||
```
|
||||
|
||||
To enable the new effect system, compile with `--experimental:strictEffects`.
|
||||
See also [#18777](https://github.com/nim-lang/Nim/pull/18777) and RFC
|
||||
[#408](https://github.com/nim-lang/RFCs/issues/408).
|
||||
|
||||
|
||||
## Private imports and private field access
|
||||
|
||||
A new import syntax `import foo {.all.}` now allows importing all symbols
|
||||
(public or private) from `foo`.
|
||||
This can be useful for testing purposes or for more flexibility in project organization.
|
||||
|
||||
Example:
|
||||
```nim
|
||||
from system {.all.} as system2 import nil
|
||||
echo system2.ThisIsSystem # ThisIsSystem is private in `system`
|
||||
import os {.all.} # weirdTarget is private in `os`
|
||||
echo weirdTarget # or `os.weirdTarget`
|
||||
```
|
||||
|
||||
Added a new module `std/importutils`, and an API `privateAccess`, which allows access
|
||||
to private fields for an object type in the current scope.
|
||||
|
||||
Example:
|
||||
```nim
|
||||
import times
|
||||
from std/importutils import privateAccess
|
||||
block:
|
||||
let t = now()
|
||||
# echo t.monthdayZero # Error: undeclared field: 'monthdayZero' for type times.DateTime
|
||||
privateAccess(typeof(t)) # enables private access in this scope
|
||||
echo t.monthdayZero # ok
|
||||
```
|
||||
|
||||
See PR [#17706](https://github.com/nim-lang/Nim/pull/17706) for additional details.
|
||||
|
||||
|
||||
## `nim --eval:cmd`
|
||||
|
||||
Added `nim --eval:cmd` to evaluate a command directly, e.g.: `nim --eval:"echo 1"`.
|
||||
It defaults to `e` (nimscript) but can also work with other commands, e.g.:
|
||||
```bash
|
||||
find . | nim r --eval:'import strutils; for a in stdin.lines: echo a.toUpper'
|
||||
```
|
||||
|
||||
```bash
|
||||
# use as a calculator:
|
||||
nim --eval:'echo 3.1 / (1.2+7)'
|
||||
# explore a module's APIs, including private symbols:
|
||||
nim --eval:'import os {.all.}; echo weirdTarget'
|
||||
# use a custom backend:
|
||||
nim r -b:js --eval:"import std/jsbigints; echo 2'big ** 64'big"
|
||||
```
|
||||
|
||||
See PR [#15687](https://github.com/nim-lang/Nim/pull/15687) for more details.
|
||||
|
||||
|
||||
## Round-trip float to string
|
||||
|
||||
`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).
|
||||
This currently has to be enabled via `-d:nimPreviewFloatRoundtrip`.
|
||||
It is expected that this behavior becomes the new default in upcoming versions,
|
||||
as with other `nimPreviewX` define flags.
|
||||
|
||||
Example:
|
||||
```nim
|
||||
from math import round
|
||||
let a = round(9.779999999999999, 2)
|
||||
assert a == 9.78
|
||||
echo a # with `-d:nimPreviewFloatRoundtrip`: 9.78, like in python3 (instead of 9.779999999999999)
|
||||
```
|
||||
|
||||
|
||||
## New `std/jsbigints` module
|
||||
|
||||
Provides arbitrary precision integers for the JS target. See PR
|
||||
[#16409](https://github.com/nim-lang/Nim/pull/16409).
|
||||
Example:
|
||||
```nim
|
||||
import std/jsbigints
|
||||
assert 2'big ** 65'big == 36893488147419103232'big
|
||||
echo 0xdeadbeef'big shl 4'big # 59774856944n
|
||||
```
|
||||
|
||||
|
||||
## New `std/sysrand` module
|
||||
Cryptographically secure pseudorandom number generator,
|
||||
allows generating random numbers from a secure source provided by the operating system.
|
||||
Example:
|
||||
```nim
|
||||
import std/sysrand
|
||||
assert urandom(1234) != urandom(1234) # unlikely to fail in practice
|
||||
```
|
||||
See PR [#16459](https://github.com/nim-lang/Nim/pull/16459).
|
||||
|
||||
|
||||
## New module: `std/tempfiles`
|
||||
|
||||
Allows creating temporary files and directories, see PR
|
||||
[#17361](https://github.com/nim-lang/Nim/pull/17361) and followups.
|
||||
```nim
|
||||
import std/tempfiles
|
||||
let tmpPath = genTempPath("prefix", "suffix.log", "/tmp/")
|
||||
# tmpPath looks like: /tmp/prefixpmW1P2KLsuffix.log
|
||||
|
||||
let dir = createTempDir("tmpprefix_", "_end")
|
||||
# created dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end"
|
||||
|
||||
let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp")
|
||||
# path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp"
|
||||
cfile.write "foo"
|
||||
cfile.setFilePos 0
|
||||
assert readAll(cfile) == "foo"
|
||||
close cfile
|
||||
assert readFile(path) == "foo"
|
||||
```
|
||||
|
||||
|
||||
## User-defined literals
|
||||
|
||||
Custom numeric literals (e.g. `-128'bignum`) are now supported.
|
||||
Additionally, the unary minus in `-1` is now part of the integer literal, i.e.
|
||||
it is now parsed as a single token.
|
||||
This implies that edge cases like `-128'i8` finally work correctly.
|
||||
Example:
|
||||
```nim
|
||||
func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".}
|
||||
assert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
|
||||
```
|
||||
|
||||
|
||||
## Dot-like operators
|
||||
|
||||
With `-d:nimPreviewDotLikeOps`, dot-like operators (operators starting with `.`,
|
||||
but not with `..`) now have the same precedence as `.`, so that `a.?b.c` is now
|
||||
parsed as `(a.?b).c` instead of `a.?(b.c)`.
|
||||
A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`.
|
||||
|
||||
An important use case is to enable dynamic fields without affecting the
|
||||
built-in `.` operator, e.g. for `std/jsffi`, `std/json`, `pkg/nimpy`. Example:
|
||||
```nim
|
||||
import std/json
|
||||
template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
|
||||
a[astToStr(b)]
|
||||
let j = %*{"a1": {"a2": 10}}
|
||||
assert j.?a1.?a2.getInt == 10
|
||||
```
|
||||
|
||||
|
||||
## Block arguments now support optional parameters
|
||||
|
||||
This solves a major pain point for routines accepting block parameters,
|
||||
see PR [#18631](https://github.com/nim-lang/Nim/pull/18631) for details:
|
||||
|
||||
```nim
|
||||
template fn(a = 1, b = 2, body) = discard
|
||||
fn(1, 2): # already works
|
||||
bar
|
||||
fn(a = 1): # now works
|
||||
bar
|
||||
```
|
||||
|
||||
Likewise with multiple block arguments via `do`:
|
||||
```nim
|
||||
template fn(a = 1, b = 2, body1, body2) = discard
|
||||
fn(a = 1): # now works
|
||||
bar1
|
||||
do:
|
||||
bar2
|
||||
```
|
||||
|
||||
|
||||
# Other new features
|
||||
|
||||
## New and deprecated modules
|
||||
|
||||
The following modules were added (they are discussed in the rest of the text):
|
||||
- `std/enumutils`
|
||||
- `std/genasts`
|
||||
- `std/importutils`
|
||||
- `std/jsbigints`
|
||||
- `std/jsfetch`
|
||||
- `std/jsformdata`
|
||||
- `std/jsheaders`
|
||||
- `std/packedsets`
|
||||
- `std/setutils`
|
||||
- `std/socketstreams`
|
||||
- `std/strbasics`
|
||||
- `std/sysrand`
|
||||
- `std/tasks`
|
||||
- `std/tempfiles`
|
||||
- `std/vmutils`
|
||||
|
||||
- Deprecated `std/mersenne`.
|
||||
- Removed deprecated `std/iup` module from stdlib; it has already moved to
|
||||
[nimble](https://github.com/nim-lang/iup).
|
||||
|
||||
|
||||
## New `std/jsfetch` module
|
||||
|
||||
Provides a wrapper for JS Fetch API.
|
||||
Example:
|
||||
```nim
|
||||
# requires -d:nimExperimentalAsyncjsThen
|
||||
import std/[jsfetch, asyncjs, jsconsole, jsffi, sugar]
|
||||
proc fn {.async.} =
|
||||
await fetch("https://api.github.com/users/torvalds".cstring)
|
||||
.then((response: Response) => response.json())
|
||||
.then((json: JsObject) => console.log(json))
|
||||
.catch((err: Error) => console.log("Request Failed", err))
|
||||
discard fn()
|
||||
```
|
||||
|
||||
|
||||
## New `std/tasks` module
|
||||
|
||||
Provides basic primitives for creating parallel programs.
|
||||
Example:
|
||||
```nim
|
||||
import std/tasks
|
||||
var num = 0
|
||||
proc hello(a: int) = num+=a
|
||||
|
||||
let b = toTask hello(13) # arguments must be isolated, see `std/isolation`
|
||||
b.invoke()
|
||||
assert num == 13
|
||||
b.invoke() # can be called again
|
||||
assert num == 26
|
||||
```
|
||||
|
||||
|
||||
## New module: `std/genasts`
|
||||
|
||||
Provides an API `genAst` that avoids the problems inherent with `quote do` and can
|
||||
be used as a replacement.
|
||||
Example showing how this could be used for writing a simplified version of `unittest.check`:
|
||||
```nim
|
||||
import std/[genasts, macros, strutils]
|
||||
macro check2(cond: bool): untyped =
|
||||
assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind
|
||||
result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]):
|
||||
# each local symbol we access must be explicitly captured
|
||||
if not cond:
|
||||
doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs]
|
||||
let a = 3
|
||||
check2 a*2 == a+3
|
||||
if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4'
|
||||
```
|
||||
|
||||
See PR [#17426](https://github.com/nim-lang/Nim/pull/17426) for details.
|
||||
|
||||
|
||||
## New module: `std/setutils`
|
||||
|
||||
- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`,
|
||||
if the iterable yields a built-in settable type.
|
||||
- Added `setutils.fullSet` which returns a full built-in `set` for a valid type.
|
||||
- Added `setutils.complement` which returns the complement of a built-in `set`.
|
||||
- Added `setutils.[]=`.
|
||||
|
||||
|
||||
## New module: `std/enumutils`
|
||||
|
||||
- Added `genEnumCaseStmt` macro that generates
|
||||
case statement to parse string to enum.
|
||||
- Added `items` for enums with holes.
|
||||
- Added `symbolName` to return the `enum` symbol name ignoring the human-readable name.
|
||||
- Added `symbolRank` to return the index in which an `enum` member is listed in an enum.
|
||||
|
||||
|
||||
## `system`
|
||||
|
||||
- Added `system.prepareMutation` for better support of low
|
||||
level `moveMem`, `copyMem` operations for `gc:orc`'s copy-on-write string
|
||||
implementation.
|
||||
- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible
|
||||
with most other languages.
|
||||
- Added `cmpMem` to `system`.
|
||||
- `doAssertRaises` now correctly handles foreign exceptions.
|
||||
- `addInt` now supports unsigned integers.
|
||||
|
||||
Compatibility notes:
|
||||
- `system.delete` had surprising behavior when the index passed to it was out of
|
||||
bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so
|
||||
that an index error is produced instead. Be aware however that your code might depend on
|
||||
this quirky behavior so a review process is required on your part before you can
|
||||
use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete`
|
||||
switch, which pretends that `system.delete` is deprecated so that it is easier
|
||||
to see where it was used in your code.
|
||||
`-d:nimStrictDelete` will become the default in upcoming versions.
|
||||
- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`.
|
||||
Please use `char` or `uint8` instead.
|
||||
- `repr` now doesn't insert trailing newlines; the previous behavior was very inconsistent,
|
||||
see [#16034](https://github.com/nim-lang/Nim/pull/16034).
|
||||
Use `-d:nimLegacyReprWithNewline` for the previous behavior.
|
||||
`repr` now also renders ASTs correctly for user defined literals, setters, `do`, etc.
|
||||
- Deprecated `any`. See RFC [#281](https://github.com/nim-lang/RFCs/issues/281).
|
||||
- The unary slice `..b` was deprecated, use `0..b` instead.
|
||||
|
||||
|
||||
## `std/math`
|
||||
|
||||
- Added `almostEqual` for comparing two float values using a machine epsilon.
|
||||
- Added `clamp` which allows using a `Slice` to clamp to a value.
|
||||
- Added `ceilDiv` for integer division that rounds up.
|
||||
- Added `isNaN`.
|
||||
- Added `copySign`.
|
||||
- Added `euclDiv` and `euclMod`.
|
||||
- Added `signbit`.
|
||||
- Added `frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead.
|
||||
|
||||
Compatibility notes:
|
||||
- `math.round` now rounds "away from zero" in the JS backend, which is consistent
|
||||
with other backends. See [#9125](https://github.com/nim-lang/Nim/pull/9125).
|
||||
Use `-d:nimLegacyJsRound` for the previous behavior.
|
||||
|
||||
|
||||
## Random number generators: `std/random`, `std/sysrand`, `std/oids`
|
||||
|
||||
- Added `std/sysrand` module (see details above).
|
||||
- Added `randState` template that exposes the default random number generator.
|
||||
Useful for library authors.
|
||||
- Added `initRand()` overload with no argument which uses the current time as a seed.
|
||||
- `initRand(seed)` now allows `seed == 0`.
|
||||
- Fixed overflow bugs.
|
||||
- Fix `initRand` to avoid random number sequences overlapping, refs
|
||||
[#18744](https://github.com/nim-lang/Nim/pull/18744).
|
||||
- `std/oids` now uses `std/random`.
|
||||
|
||||
Compatibility notes:
|
||||
- Deprecated `std/mersenne`.
|
||||
- `random.initRand(seed)` now produces non-skewed values for the first call to
|
||||
`rand()` after initialization with a small (< 30000) seed.
|
||||
Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition
|
||||
time, see PR [#17467](https://github.com/nim-lang/Nim/pull/17467).
|
||||
|
||||
|
||||
## `std/json`, `std/jsonutils`
|
||||
|
||||
- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize
|
||||
holey enums as regular enums (via `ord`) instead of as strings.
|
||||
It is expected that this behavior becomes the new default in upcoming versions.
|
||||
`toJson` now serializes `JsonNode` as is via reference (without a deep copy)
|
||||
instead of treating `JsonNode` as a regular ref object,
|
||||
this can be customized via `jsonNodeMode`.
|
||||
- `std/json` and `std/jsonutils` now serialize `NaN`, `Inf`, `-Inf` as strings,
|
||||
so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]`
|
||||
which was invalid JSON.
|
||||
- `std/json` can now handle integer literals and floating point literals of
|
||||
arbitrary length and precision.
|
||||
Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are
|
||||
kept as string literals and one can use external BigNum libraries to handle these.
|
||||
The `parseFloat` family of functions also has now optional `rawIntegers` and
|
||||
`rawFloats` parameters that can be used to enforce that all integer or float
|
||||
literals remain in the "raw" string form so that client code can easily treat
|
||||
small and large numbers uniformly.
|
||||
- Added `BackwardsIndex` overload for `JsonNode`.
|
||||
- `json.%`,`json.to`, `jsonutils.fromJson`,`jsonutils.toJson` now work with `uint|uint64`
|
||||
instead of raising (as in 1.4) or giving wrong results (as in 1.2).
|
||||
- `std/jsonutils` now handles `cstring` (including as Table key), and `set`.
|
||||
- Added `jsonutils.jsonTo` overload with `opt = Joptions()` param.
|
||||
- `jsonutils.toJson` now supports customization via `ToJsonOptions`.
|
||||
- `std/json`, `std/jsonutils` now support round-trip serialization when
|
||||
`-d:nimPreviewFloatRoundtrip` is used.
|
||||
|
||||
|
||||
## `std/typetraits`, `std/compilesettings`
|
||||
|
||||
- `distinctBase` now is identity instead of error for non distinct types.
|
||||
- `distinctBase` now allows controlling whether to be recursive or not.
|
||||
- Added `enumLen` to return the number of elements in an enum.
|
||||
- Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes.
|
||||
- Added `hasClosure`.
|
||||
- Added `pointerBase` to return `T` for `ref T | ptr T`.
|
||||
- Added `compilesettings.SingleValueSetting.libPath`.
|
||||
|
||||
|
||||
## networking: `std/net`, `std/asyncnet`, `std/htmlgen`, `std/httpclient`, `std/asyncdispatch`, `std/asynchttpserver`, `std/httpcore`
|
||||
|
||||
- Fixed buffer overflow bugs in `std/net`.
|
||||
- Exported `sslHandle` from `std/net` and `std/asyncnet`.
|
||||
- Added `hasDataBuffered` to `std/asyncnet`.
|
||||
- Various functions in `std/httpclient` now accept `url` of type `Uri`.
|
||||
Moreover, the `request` function's `httpMethod` argument of type `string` was
|
||||
deprecated in favor of `HttpMethod` `enum` type; see
|
||||
[#15919](https://github.com/nim-lang/Nim/pull/15919).
|
||||
- Added `asyncdispatch.activeDescriptors` that returns the number of currently
|
||||
active async event handles/file descriptors.
|
||||
- Added `getPort` to `std/asynchttpserver` to resolve OS-assigned `Port(0)`;
|
||||
this is usually recommended instead of hardcoding ports which can lead to
|
||||
"Address already in use" errors.
|
||||
- Fixed premature garbage collection in `std/asyncdispatch`, when a stacktrace
|
||||
override is in place.
|
||||
- Added `httpcore.is1xx` and missing HTTP codes.
|
||||
- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals).
|
||||
|
||||
Compatibility notes:
|
||||
- On Windows, the SSL library now checks for valid certificates.
|
||||
For this purpose it uses the `cacert.pem` file, which was extracted
|
||||
from `https://curl.se/ca/cacert.pem`. Besides
|
||||
the OpenSSL DLLs (e.g. `libssl-1_1-x64.dll`, `libcrypto-1_1-x64.dll`) you
|
||||
now also need to ship `cacert.pem` with your `.exe` file.
|
||||
|
||||
|
||||
## `std/hashes`
|
||||
|
||||
- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code),
|
||||
if `-d:nimPreviewHashRef` is used. It is expected that this behavior
|
||||
becomes the new default in upcoming versions.
|
||||
- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`.
|
||||
`hashes.hash(closure)` has also been improved.
|
||||
|
||||
|
||||
## OS: `std/os`, `std/io`, `std/socketstream`, `std/linenoise`, `std/tempfiles`
|
||||
|
||||
- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`,
|
||||
determining preferred I/O block size for this file object.
|
||||
- Added `os.getCacheDir()` to return platform specific cache directory.
|
||||
- Improved `os.getTempDir()`, see PR [#16914](https://github.com/nim-lang/Nim/pull/16914).
|
||||
- Added `os.isAdmin` to tell whether the caller's process is a member of the
|
||||
Administrators local group (on Windows) or a root (on POSIX).
|
||||
- Added optional `options` argument to `copyFile`, `copyFileToDir`, and
|
||||
`copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are
|
||||
followed (copy files symlinks point to); on Windows, `options` argument is
|
||||
ignored and symlinks are skipped.
|
||||
- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as
|
||||
symlinks (instead of skipping them as it was before); on Windows symlinks are
|
||||
skipped.
|
||||
- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks
|
||||
(instead of skipping them sometimes as it was before).
|
||||
- Added optional `followSymlinks` argument to `setFilePermissions`.
|
||||
- Added a simpler to use `io.readChars` overload.
|
||||
- Added `socketstream` module that wraps sockets in the stream interface.
|
||||
- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
|
||||
|
||||
|
||||
## Environment variable handling
|
||||
|
||||
- Empty environment variable values are now supported across OS's and backends.
|
||||
- Environment variable APIs now work in multithreaded scenarios, by delegating to
|
||||
direct OS calls instead of trying to keep track of the environment.
|
||||
- `putEnv`, `delEnv` now work at compile time.
|
||||
- NodeJS backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`.
|
||||
|
||||
Compatibility notes:
|
||||
- `std/os`: `putEnv` now raises if the first argument contains a `=`.
|
||||
|
||||
|
||||
## POSIX
|
||||
|
||||
- On POSIX systems, the default signal handlers used for Nim programs (it's
|
||||
used for printing the stacktrace on fatal signals) will now re-raise the
|
||||
signal for the OS default handlers to handle.
|
||||
This lets the OS perform its default actions, which might include core
|
||||
dumping (on select signals) and notifying the parent process about the cause
|
||||
of termination.
|
||||
- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler`
|
||||
for previous behavior.
|
||||
- Added `posix_utils.osReleaseFile` to get system identification from `os-release`
|
||||
file on Linux and the BSDs.
|
||||
([link](https://www.freedesktop.org/software/systemd/man/os-release.html))
|
||||
- Removed undefined behavior for `posix.open`.
|
||||
|
||||
|
||||
## `std/prelude`
|
||||
|
||||
- `std/strformat` is now part of `include std/prelude`.
|
||||
- Added `std/sequtils` import to `std/prelude`.
|
||||
- `std/prelude` now works with the JS target.
|
||||
- `std/prelude` can now be used via `include std/prelude`, but `include prelude` still works.
|
||||
|
||||
|
||||
## String manipulation: `std/strformat`, `std/strbasics`
|
||||
|
||||
- Added support for parenthesized expressions.
|
||||
- Added support for const strings instead of just string literals.
|
||||
- Added `std/strbasics` for high-performance string operations.
|
||||
- Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`.
|
||||
|
||||
|
||||
## `std/wrapnils`
|
||||
|
||||
- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding
|
||||
issues like bug [#13063](https://github.com/nim-lang/Nim/issues/13063)
|
||||
(which affected error messages) for modules importing `std/wrapnils`.
|
||||
- Added `??.` macro which returns an `Option`.
|
||||
- `std/wrapnils` can now be used to protect against `FieldDefect` errors in
|
||||
case objects, generates optimal code (no overhead compared to manual
|
||||
if-else branches), and preserves lvalue semantics which allows modifying
|
||||
an expression.
|
||||
|
||||
|
||||
## Containers: `std/algorithm`, `std/lists`, `std/sequtils`, `std/options`, `std/packedsets`
|
||||
|
||||
- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix`
|
||||
iterators (it never worked reliably).
|
||||
- Added `algorithm.merge`.
|
||||
- In `std/lists`: renamed `append` to `add` and retained `append` as an alias;
|
||||
added `prepend` and `prependMoved` analogously to `add` and `addMoved`;
|
||||
added `remove` for `SinglyLinkedList`s.
|
||||
- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList`
|
||||
and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
|
||||
shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes
|
||||
its argument, `addMoved`, is also supplied.
|
||||
See PRs [#16362](https://github.com/nim-lang/Nim/pull/16362),
|
||||
[#16536](https://github.com/nim-lang/Nim/pull/16536).
|
||||
- New module: `std/packedsets`.
|
||||
Generalizes `std/intsets`, see PR [#15564](https://github.com/nim-lang/Nim/pull/15564).
|
||||
|
||||
Compatibility notes:
|
||||
- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises
|
||||
a defect if the slice is out of bounds, likewise with `strutils.delete`.
|
||||
- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]`
|
||||
in `std/algorithm`.
|
||||
- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
|
||||
and `$none(int)` to `"none(int)"` instead of `"None[int]"`.
|
||||
|
||||
|
||||
## `std/times`
|
||||
|
||||
- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time
|
||||
zone offsets without colons, e.g. `UTC+7 -> +0700`.
|
||||
- Added `dateTime` and deprecated `initDateTime`.
|
||||
|
||||
|
||||
## `std/macros` and AST
|
||||
|
||||
- New module `std/genasts`, see description above.
|
||||
- The required name of case statement macros for the experimental
|
||||
`caseStmtMacros` feature has changed from `match` to `` `case` ``.
|
||||
- Tuple expressions are now parsed consistently as
|
||||
`nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`.
|
||||
- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes
|
||||
in a collapsed way.
|
||||
Use `-d:nimLegacyMacrosCollapseSymChoice` to get the previous behavior.
|
||||
- Made custom op in `macros.quote` work for all statements.
|
||||
|
||||
|
||||
## `std/sugar`
|
||||
|
||||
- Added `sugar.dumpToString` which improves on `sugar.dump`.
|
||||
- Added an overload for the `collect` macro that infers the container type based
|
||||
on the syntax of the last expression. Works with std seqs, tables and sets.
|
||||
|
||||
Compatibility notes:
|
||||
- Removed support for named procs in `sugar.=>`.
|
||||
|
||||
|
||||
## Parsing: `std/parsecfg`, `std/strscans`, `std/uri`
|
||||
|
||||
- Added `sections` iterator in `parsecfg`.
|
||||
- `strscans.scanf` now supports parsing single characters.
|
||||
- Added `strscans.scanTuple` which uses `strscans.scanf` internally,
|
||||
returning a tuple which can be unpacked for easier usage of `scanf`.
|
||||
- Added `decodeQuery` to `std/uri`.
|
||||
- `parseopt.initOptParser` has been made available and `parseopt` has been
|
||||
added back to `std/prelude` for all backends. Previously `initOptParser` was
|
||||
unavailable if the `std/os` module did not have `paramCount` or `paramStr`,
|
||||
but the use of these in `initOptParser` were conditionally to the runtime
|
||||
arguments passed to it, so `initOptParser` has been changed to raise
|
||||
`ValueError` when the real command line is not available. `parseopt` was
|
||||
previously excluded from `std/prelude` for JS, as it could not be imported.
|
||||
|
||||
Compatibility notes:
|
||||
- Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
|
||||
characters in the decoded values. Prior versions would raise an error. This is
|
||||
no longer the case to comply with the HTML spec and other languages'
|
||||
implementations. Old behavior can be obtained with
|
||||
`-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
|
||||
underlying code is also updated the same way.
|
||||
|
||||
|
||||
## JS stdlib changes
|
||||
|
||||
- Added `std/jsbigints` module, which provides arbitrary precision integers for the JS target.
|
||||
- Added `setCurrentException` for the JS backend.
|
||||
- `writeStackTrace` is available in the JS backend now.
|
||||
- Added `then`, `catch` to `std/asyncjs` for promise pipelining, for now hidden
|
||||
behind `-d:nimExperimentalAsyncjsThen`.
|
||||
- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API)
|
||||
wrapper for the JS target.
|
||||
- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
||||
wrapper for the JS target.
|
||||
- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
|
||||
wrapper for the JS target.
|
||||
- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger).
|
||||
- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
|
||||
- Added dollar `$` and `len` for `jsre.RegExp`.
|
||||
- Added `jsconsole.jsAssert` for the JS target.
|
||||
- Added `**` to `std/jsffi`.
|
||||
- Added `copyWithin` [for `seq` and `array` for JS targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin).
|
||||
- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`.
|
||||
Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated.
|
||||
- Added `dom.scrollIntoView` proc with options.
|
||||
- Added `dom.setInterval`, `dom.clearInterval` overloads.
|
||||
- Merged `std/dom_extensions` into the `std/dom` module,
|
||||
as it was a module with a single line, see RFC [#413](https://github.com/nim-lang/RFCs/issues/413).
|
||||
- `$` now gives more correct results on the JS backend.
|
||||
|
||||
|
||||
## JS compiler changes
|
||||
|
||||
- `cstring` doesn't support the `[]=` operator anymore in the JS backend.
|
||||
- Array literals now use JS typed arrays when the corresponding JS typed array exists,
|
||||
for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`.
|
||||
|
||||
|
||||
## VM and nimscript backend
|
||||
|
||||
- VM now supports `addr(mystring[ind])` (index + index assignment).
|
||||
- `nimscript` now handles `except Exception as e`.
|
||||
- `nil` dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time.
|
||||
- `static[T]` now works better, refs [#17590](https://github.com/nim-lang/Nim/pull/17590),
|
||||
[#15853](https://github.com/nim-lang/Nim/pull/15853).
|
||||
- `distinct T` conversions now work in VM.
|
||||
- `items(cstring)` now works in VM.
|
||||
- Fix `addr`, `len`, `high` in VM ([#16002](https://github.com/nim-lang/Nim/pull/16002),
|
||||
[#16610](https://github.com/nim-lang/Nim/pull/16610)).
|
||||
- `std/cstrutils` now works in VM.
|
||||
|
||||
|
||||
## OS/platform-specific notes
|
||||
|
||||
- Support for Apple silicon/M1.
|
||||
- Support for 32-bit RISC-V, refs [#16231](https://github.com/nim-lang/Nim/pull/16231).
|
||||
- Support for armv8l, refs [#18901](https://github.com/nim-lang/Nim/pull/18901).
|
||||
- Support for CROSSOS, refs [#18889](https://github.com/nim-lang/Nim/pull/18889).
|
||||
- The allocator for Nintendo Switch, which was nonfunctional because
|
||||
of breaking changes in libnx, was removed, in favor of the new `-d:nimAllocPagesViaMalloc` option.
|
||||
- Allow reading parameters when compiling for Nintendo Switch.
|
||||
- `--nimcache` now correctly works in a cross-compilation setting.
|
||||
- Cross compilation targeting Windows was improved.
|
||||
- This now works from macOS/Linux: `nim r -d:mingw main`
|
||||
|
||||
|
||||
|
||||
## Performance / memory optimizations
|
||||
|
||||
- The comment field in PNode AST was moved to a side channel, reducing overall
|
||||
memory usage during compilation by a factor 1.25x
|
||||
- `std/jsonutils` deserialization is now up to 20x faster.
|
||||
- `os.copyFile` is now 2.5x faster on macOS, by using `copyfile` from `copyfile.h`;
|
||||
use `-d:nimLegacyCopyFile` for macOS < 10.5.
|
||||
- Float to string conversion is now 10x faster thanks to the Dragonbox algorithm,
|
||||
with `-d:nimPreviewFloatRoundtrip`.
|
||||
- `newSeqWith` is 3x faster.
|
||||
- CI now supports batching (making Windows CI 2.3X faster).
|
||||
- Sets now uses the optimized `countSetBits` proc, see PR
|
||||
[#17334](https://github.com/nim-lang/Nim/pull/17334).
|
||||
|
||||
|
||||
## Debugging
|
||||
|
||||
- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`.
|
||||
- `koch tools` now builds `bin/nim_dbg` which allows easy access to a debug version
|
||||
of Nim without recompiling.
|
||||
- Added new module `compiler/debugutils` to help with debugging Nim compiler.
|
||||
- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints` and
|
||||
used it in more contexts; this flag which works in tandem with `--stackTraceMsgs`
|
||||
to show user code context in compiler stacktraces.
|
||||
|
||||
|
||||
## Type system
|
||||
|
||||
- `typeof(voidStmt)` now works and returns `void`.
|
||||
- `enum` values can now be overloaded. This needs to be enabled
|
||||
via `{.experimental: "overloadableEnums".}`. We hope that this feature allows
|
||||
for the development of more fluent (less ugly) APIs.
|
||||
See RFC [#373](https://github.com/nim-lang/RFCs/issues/373) for more details.
|
||||
- A type conversion from one `enum` type to another now produces an `[EnumConv]` warning.
|
||||
You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead.
|
||||
```nim
|
||||
type A = enum a1, a2
|
||||
type B = enum b1, b2
|
||||
echo a1.B # produces a warning
|
||||
echo a1.ord.B # produces no warning
|
||||
```
|
||||
- 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.
|
||||
- There is a new warning for *any* type conversion to `enum` that can be enabled via
|
||||
`.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`.
|
||||
- Reusing a type name in a different scope now works, refs [#17710](https://github.com/nim-lang/Nim/pull/17710).
|
||||
- Fixed implicit and explicit generics in procedures, refs [#18808](https://github.com/nim-lang/Nim/pull/18808).
|
||||
|
||||
|
||||
## New-style concepts
|
||||
|
||||
Example:
|
||||
```nim
|
||||
type
|
||||
Comparable = concept # no T, an atom
|
||||
proc cmp(a, b: Self): int
|
||||
```
|
||||
The new design does not rely on `system.compiles` and may compile faster.
|
||||
See PR [#15251](https://github.com/nim-lang/Nim/pull/15251)
|
||||
and RFC [#168](https://github.com/nim-lang/RFCs/issues/168) for details.
|
||||
|
||||
|
||||
## Lexical / syntactic
|
||||
|
||||
- Nim now supports a small subset of Unicode operators as operator symbols.
|
||||
The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔".
|
||||
To enable this feature, use `--experimental:unicodeOperators`. Note that due
|
||||
to parser limitations you **cannot** enable this feature via a
|
||||
pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable
|
||||
it via the command line or in a configuration file.
|
||||
- `var a {.foo.} = expr` now works inside templates (except when `foo` is overloaded).
|
||||
|
||||
|
||||
## Compiler messages, error messages, hints, warnings
|
||||
|
||||
- Significant improvement to error messages involving effect mismatches,
|
||||
see PRs [#18384](https://github.com/nim-lang/Nim/pull/18384),
|
||||
[#18418](https://github.com/nim-lang/Nim/pull/18418).
|
||||
- Added `--declaredLocs` to show symbol declaration location in error messages.
|
||||
- Added `--spellSuggest` to show spelling suggestions on typos.
|
||||
- Added `--processing:dots|filenames|off` which customizes `hintProcessing`;
|
||||
`--processing:filenames` shows which include/import modules are being compiled as an import stack.
|
||||
- `FieldDefect` messages now shows discriminant value + lineinfo, in all backends (C, JS, VM)
|
||||
- Added `--hintAsError` with similar semantics as `--warningAsError`.
|
||||
- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31`
|
||||
before a newline for every generated message (potentially multiline),
|
||||
so tooling can tell when messages start and end.
|
||||
- Added `--filenames:abs|canonical|legacyRelProj` which replaces `--listFullPaths:on|off`
|
||||
- `--hint:all:on|off` is now supported to select or deselect all hints; it
|
||||
differs from `--hints:on|off` which acts as a (reversible) gate.
|
||||
Likewise with `--warning:all:on|off`.
|
||||
- The style checking of the compiler now supports a `--styleCheck:usages` switch.
|
||||
This switch enforces that every symbol is written as it was declared, not enforcing
|
||||
the official Nim style guide. To be enabled, this has to be combined either
|
||||
with `--styleCheck:error` or `--styleCheck:hint`.
|
||||
- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for
|
||||
previous behavior.
|
||||
- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages.
|
||||
- `runnableExamples` now show originating location in stacktraces on failure.
|
||||
- `SuccessX` message now shows more useful information.
|
||||
- New `DuplicateModuleImport` warning; improved `UnusedImport` and
|
||||
`XDeclaredButNotUsed` accuracy.
|
||||
|
||||
Compatibility notes:
|
||||
- `--hint:CC` now prints to stderr (like all other hints) instead of stdout.
|
||||
|
||||
|
||||
## Building and running Nim programs, configuration system
|
||||
|
||||
- JSON build instructions are now generated in `$nimcache/outFileBasename.json`
|
||||
instead of `$nimcache/projectName.json`. This allows avoiding recompiling a
|
||||
given project compiled with different options if the output file differs.
|
||||
- `--usenimcache` (implied by `nim r main`) now generates an output file that includes
|
||||
a hash of some of the compilation options, which allows caching generated binaries:
|
||||
```bash
|
||||
nim r main # recompiles
|
||||
nim r -d:foo main # recompiles
|
||||
nim r main # uses cached binary
|
||||
nim r main arg1 arg2 # likewise (runtime arguments are irrelevant)
|
||||
```
|
||||
- `nim r` now supports cross compilation from unix to windows when specifying
|
||||
`-d:mingw` by using Wine, e.g.:
|
||||
`nim r --eval:'import os; echo "a" / "b"'` prints `a\b`.
|
||||
- `nim` can compile version 1.4.0 as follows:
|
||||
`nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`.
|
||||
`-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel.
|
||||
- `nim e` now accepts arbitrary file extensions for the nimscript file,
|
||||
although `.nims` is still the preferred extension in general.
|
||||
- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected.
|
||||
The downside is that these defines now have custom logic that doesn't apply for
|
||||
other defines.
|
||||
|
||||
|
||||
## Multithreading
|
||||
|
||||
- TLS: macOS now uses native TLS (`--tlsEmulation:off`). TLS now works with
|
||||
`importcpp` non-POD types; such types must use `.cppNonPod` and
|
||||
`--tlsEmulation:off`should be used.
|
||||
- Added `unsafeIsolate` and `extract` to `std/isolation`.
|
||||
- Added `std/tasks`, see description above.
|
||||
|
||||
|
||||
## Memory management
|
||||
|
||||
- `--gc:arc` now bootstraps (PR [#17342](https://github.com/nim-lang/Nim/pull/17342)).
|
||||
- Lots of improvements to `gc:arc`, `gc:orc`, see PR
|
||||
[#15697](https://github.com/nim-lang/Nim/pull/15697),
|
||||
[#16849](https://github.com/nim-lang/Nim/pull/16849),
|
||||
[#17993](https://github.com/nim-lang/Nim/pull/17993).
|
||||
- `--gc:orc` is now 10% faster than previously for common workloads.
|
||||
If you have trouble with its changed behavior, compile with `-d:nimOldOrc`.
|
||||
- The `--gc:orc` algorithm was refined so that custom container types can participate in the
|
||||
cycle collection process. See the documentation of `=trace` for more details.
|
||||
- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`.
|
||||
This is only supported for `--gc:orc` or `--gc:arc`.
|
||||
|
||||
Compatibility notes:
|
||||
- `--newruntime` and `--refchecks` are deprecated,
|
||||
use `--gc:arc`, `--gc:orc`, or `--gc:none` as appropriate instead.
|
||||
|
||||
|
||||
## Docgen
|
||||
|
||||
- docgen: RST files can now use single backticks instead of double backticks and
|
||||
correctly render in both `nim rst2html` (as before) as well as common tools rendering
|
||||
RST directly (e.g. GitHub).
|
||||
This is done by adding the `default-role:: code` directive inside the RST file
|
||||
(which is now handled by `nim rst2html`).
|
||||
- Source+Edit links now appear on top of every docgen'd page when
|
||||
`nim doc --git.url:url ...` is given.
|
||||
- Latex doc generation is revised: output `.tex` files should be compiled
|
||||
by `xelatex` (not by `pdflatex` as before). Now default Latex settings
|
||||
provide support for Unicode and better avoid margin overflows.
|
||||
The minimum required version is TeXLive 2018 (or an equivalent MikTeX version).
|
||||
- The RST parser now supports footnotes, citations, admonitions, and short style
|
||||
references with symbols.
|
||||
- The RST parser now supports Markdown table syntax.
|
||||
Known limitations:
|
||||
- cell alignment is not supported, i.e. alignment annotations in a delimiter
|
||||
row (`:---`, `:--:`, `---:`) are ignored
|
||||
- every table row must start with `|`, e.g. `| cell 1 | cell 2 |`.
|
||||
- Implemented `doc2tex` compiler command which converts documentation in
|
||||
`.nim` files to Latex.
|
||||
- docgen now supports syntax highlighting for inline code.
|
||||
- docgen now supports same-line doc comments:
|
||||
```nim
|
||||
func fn*(a: int): int = 42 ## Doc comment
|
||||
```
|
||||
- docgen now renders `deprecated` and other pragmas.
|
||||
- `runnableExamples` now works with templates and nested templates.
|
||||
- `runnableExamples: "-r:off"` now works for examples that should compile but not run.
|
||||
- `runnableExamples` now renders code verbatim, and produces correct code in all cases.
|
||||
- docgen now shows correct, canonical import paths in docs.
|
||||
- docgen now shows all routines in sidebar, and the proc signature is now shown in sidebar.
|
||||
|
||||
|
||||
## Effects and checks
|
||||
|
||||
- Significant improvement to error messages involving effect mismatches
|
||||
- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some
|
||||
compiler checks regarding `case objects`. This allows serialization libraries
|
||||
to avoid ugly, non-portable solutions. See RFC
|
||||
[#407](https://github.com/nim-lang/RFCs/issues/407) for more details.
|
||||
|
||||
Compatibility notes:
|
||||
- Fixed effect tracking for borrowed procs (see [#18882](https://github.com/nim-lang/Nim/pull/18882)).
|
||||
One consequence is that, under some circumstances, Nim could previously permit a procedure with side effects to be written with `func` - you may need to change some occurrences of `func` to `proc`.
|
||||
To illustrate, Nim versions before 1.6.0 compile the below without error
|
||||
```nim
|
||||
proc print(s: string) =
|
||||
echo s
|
||||
|
||||
type
|
||||
MyString = distinct string
|
||||
|
||||
proc print(s: MyString) {.borrow.}
|
||||
|
||||
func foo(s: MyString) =
|
||||
print(s)
|
||||
```
|
||||
but Nim 1.6.0 produces the error
|
||||
```
|
||||
Error: 'foo' can have side effects
|
||||
```
|
||||
similar to how we expect that
|
||||
```nim
|
||||
func print(s: string) =
|
||||
echo s
|
||||
```
|
||||
produces
|
||||
```
|
||||
Error: 'print' can have side effects
|
||||
```
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
- Major improvements to `nimgrep`, see PR [#15612
|
||||
](https://github.com/nim-lang/Nim/pull/15612).
|
||||
- `fusion` is now un-bundled from Nim, `./koch fusion` will
|
||||
install it via Nimble at a fixed hash.
|
||||
- `testament`: added `nimoutFull: bool` spec to compare full output of compiler
|
||||
instead of a subset; many bugfixes to testament.
|
||||
|
||||
|
||||
## Misc/cleanups
|
||||
|
||||
- Deprecated `TaintedString` and `--taintmode`.
|
||||
- Deprecated `--nilseqs` which is now a noop.
|
||||
- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have
|
||||
certain hints/warnings.
|
||||
- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`.
|
||||
- `[skip ci]` now works in azure and CI pipelines, see detail in PR
|
||||
[#17561](https://github.com/nim-lang/Nim/pull/17561).
|
||||
@@ -1,23 +1,33 @@
|
||||
# v1.xx.x - yyyy-mm-dd
|
||||
# vx.xx.x - yyyy-mm-dd
|
||||
|
||||
This is an example file.
|
||||
The changes should go to changelog.md!
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior.
|
||||
- Example item: `Foo` changed to `Bar`.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
- Added `example.exampleProc`.
|
||||
### Breaking changes in the standard library
|
||||
|
||||
|
||||
### Breaking changes in the compiler
|
||||
|
||||
|
||||
## Library additions
|
||||
- `set[T].len` is now an alias for `set[T].card` (cardinality)
|
||||
|
||||
## Library changes
|
||||
|
||||
|
||||
## Language additions
|
||||
|
||||
- Changed `example.foo` to take additional `bar` parameter.
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
## Compiler changes
|
||||
### Tool changes
|
||||
|
||||
|
||||
## Tool changes
|
||||
### Compiler changes
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
14
ci/build.bat
Normal file
14
ci/build.bat
Normal file
@@ -0,0 +1,14 @@
|
||||
REM Some debug info
|
||||
echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%."
|
||||
gcc -v
|
||||
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
cd csources
|
||||
call build64.bat
|
||||
cd ..
|
||||
set PATH=%CD%\bin;%PATH%
|
||||
nim -v
|
||||
nim c koch
|
||||
koch.exe boot
|
||||
copy bin/nim bin/nimd
|
||||
koch.exe boot -d:release
|
||||
15
ci/build.sh
Normal file
15
ci/build.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
sh ci/deps.sh
|
||||
|
||||
# Build from C sources.
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
cd csources
|
||||
sh build.sh
|
||||
cd ..
|
||||
# Add Nim to the PATH
|
||||
export PATH=$(pwd)/bin${PATH:+:$PATH}
|
||||
# Bootstrap.
|
||||
nim -v
|
||||
nim c koch
|
||||
./koch boot
|
||||
cp bin/nim bin/nimd
|
||||
./koch boot -d:release
|
||||
@@ -1,26 +0,0 @@
|
||||
@echo off
|
||||
rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
rem Build development version of the compiler; can be rerun safely
|
||||
rem bare bones version of ci/funs.sh adapted for windows.
|
||||
|
||||
rem Read in some common shared variables (shared with other tools),
|
||||
rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
|
||||
for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
|
||||
SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
|
||||
echo "building from csources: %nim_csources%"
|
||||
|
||||
if not exist %nim_csourcesDir% (
|
||||
git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
|
||||
)
|
||||
|
||||
if not exist %nim_csources% (
|
||||
cd %nim_csourcesDir%
|
||||
git checkout %nim_csourcesHash%
|
||||
echo "%PROCESSOR_ARCHITECTURE%"
|
||||
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
|
||||
SET ARCH=64
|
||||
)
|
||||
CALL build.bat
|
||||
cd ..
|
||||
copy /y bin\nim.exe %nim_csources%
|
||||
)
|
||||
4
ci/deps.bat
Normal file
4
ci/deps.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
nim e install_nimble.nims
|
||||
nim e tests/test_nimscript.nims
|
||||
nimble update
|
||||
nimble install -y zip opengl sdl1 jester@#head niminst
|
||||
16
ci/deps.sh
Normal file
16
ci/deps.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
# Some debug info
|
||||
echo "Running on $CI_RUNNER_ID ($CI_RUNNER_DESCRIPTION) with tags $CI_RUNNER_TAGS."
|
||||
|
||||
# Packages
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq build-essential git libcurl4-openssl-dev libsdl1.2-dev libgc-dev nodejs
|
||||
|
||||
gcc -v
|
||||
|
||||
export PATH=$(pwd)/bin${PATH:+:$PATH}
|
||||
|
||||
# Nimble deps
|
||||
nim e install_nimble.nims
|
||||
nim e tests/test_nimscript.nims
|
||||
nimble update
|
||||
nimble install zip opengl sdl1 jester@#head niminst
|
||||
141
ci/funs.sh
141
ci/funs.sh
@@ -1,141 +0,0 @@
|
||||
# Utilities used in CI pipelines and tooling to avoid duplication.
|
||||
# Avoid top-level statements.
|
||||
# Prefer nim scripts whenever possible.
|
||||
# functions starting with `_` are considered internal, less stable.
|
||||
|
||||
echo_run () {
|
||||
# echo's a command before running it, which helps understanding logs
|
||||
echo ""
|
||||
echo "cmd: $@" # in azure we could also use this: echo '##[section]"$@"'
|
||||
"$@"
|
||||
}
|
||||
|
||||
nimGetLastCommit() {
|
||||
git log --no-merges -1 --pretty=format:"%s"
|
||||
}
|
||||
|
||||
nimIsCiSkip(){
|
||||
# D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944
|
||||
# `--no-merges` is needed to avoid merge commits which occur for PR's.
|
||||
# $(Build.SourceVersionMessage) is not helpful
|
||||
# nor is `github.event.head_commit.message` for github actions.
|
||||
# Note: `[skip ci]` is now handled automatically for github actions, see https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/
|
||||
commitMsg=$(nimGetLastCommit)
|
||||
echo commitMsg: "$commitMsg"
|
||||
if [[ $commitMsg == *"[skip ci]"* ]]; then
|
||||
echo "skipci: true"
|
||||
return 0
|
||||
else
|
||||
echo "skipci: false"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
nimInternalInstallDepsWindows(){
|
||||
echo_run mkdir dist
|
||||
echo_run curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
|
||||
echo_run curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
echo_run 7z x dist/mingw64.7z -odist
|
||||
echo_run 7z x dist/dlls.zip -obin
|
||||
}
|
||||
|
||||
nimInternalBuildKochAndRunCI(){
|
||||
echo_run nim c koch
|
||||
if ! echo_run ./koch runCI; then
|
||||
echo_run echo "runCI failed"
|
||||
echo_run nim r tools/ci_testresults.nim
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
nimDefineVars(){
|
||||
. config/build_config.txt
|
||||
nim_csources=bin/nim_csources_$nim_csourcesHash
|
||||
}
|
||||
|
||||
_nimNumCpu(){
|
||||
# linux: $(nproc)
|
||||
# FreeBSD | macOS: $(sysctl -n hw.ncpu)
|
||||
# OpenBSD: $(sysctl -n hw.ncpuonline)
|
||||
# windows: $NUMBER_OF_PROCESSORS ?
|
||||
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
|
||||
}
|
||||
|
||||
_nimBuildCsourcesIfNeeded(){
|
||||
# if some systems cannot use make or gmake, we could add support for calling `build.sh`
|
||||
# but this is slower (not parallel jobs) and would require making build.sh
|
||||
# understand the arguments passed to the makefile (e.g. `CC=gcc ucpu=amd64 uos=darwin`),
|
||||
# instead of `--cpu amd64 --os darwin`.
|
||||
unamestr=$(uname)
|
||||
# uname values: https://en.wikipedia.org/wiki/Uname
|
||||
if [ "$unamestr" = 'FreeBSD' ]; then
|
||||
makeX=gmake
|
||||
elif [ "$unamestr" = 'OpenBSD' ]; then
|
||||
makeX=gmake
|
||||
elif [ "$unamestr" = 'NetBSD' ]; then
|
||||
makeX=gmake
|
||||
elif [ "$unamestr" = 'CROSSOS' ]; then
|
||||
makeX=gmake
|
||||
else
|
||||
makeX=make
|
||||
fi
|
||||
nCPU=$(_nimNumCpu)
|
||||
echo_run which $makeX
|
||||
# parallel jobs (5X faster on 16 cores: 10s instead of 50s)
|
||||
echo_run $makeX -C $nim_csourcesDir -j $((nCPU + 2)) -l $nCPU "$@"
|
||||
# keep $nim_csources in case needed to investigate bootstrap issues
|
||||
# without having to rebuild
|
||||
echo_run cp bin/nim $nim_csources
|
||||
}
|
||||
|
||||
nimCiSystemInfo(){
|
||||
nimDefineVars
|
||||
echo_run eval echo '$'nim_csources
|
||||
echo_run pwd
|
||||
echo_run date
|
||||
echo_run uname -a
|
||||
echo_run git log --no-merges -1 --pretty=oneline
|
||||
echo_run eval echo '$'PATH
|
||||
echo_run gcc -v
|
||||
echo_run node -v
|
||||
echo_run make -v
|
||||
}
|
||||
|
||||
nimCsourcesHash(){
|
||||
nimDefineVars
|
||||
echo $nim_csourcesHash
|
||||
}
|
||||
|
||||
nimBuildCsourcesIfNeeded(){
|
||||
# goal: allow cachine each tagged version independently
|
||||
# to avoid rebuilding, so that tools like `git bisect`
|
||||
# can grab a cached past version without rebuilding.
|
||||
nimDefineVars
|
||||
(
|
||||
set -e
|
||||
# avoid polluting caller scope with internal variable definitions.
|
||||
if test -f "$nim_csources"; then
|
||||
echo "$nim_csources exists."
|
||||
else
|
||||
if test -d "$nim_csourcesDir"; then
|
||||
echo "$nim_csourcesDir exists."
|
||||
else
|
||||
# Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g.
|
||||
# via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`.
|
||||
echo_run git clone -q --depth 1 -b $nim_csourcesBranch \
|
||||
$nim_csourcesUrl "$nim_csourcesDir"
|
||||
# old `git` versions don't support -C option, using `cd` explicitly:
|
||||
echo_run cd "$nim_csourcesDir"
|
||||
echo_run git checkout $nim_csourcesHash
|
||||
echo_run cd "$OLDPWD"
|
||||
# if needed we could also add: `git reset --hard $nim_csourcesHash`
|
||||
fi
|
||||
_nimBuildCsourcesIfNeeded "$@"
|
||||
fi
|
||||
|
||||
echo_run rm -f bin/nim
|
||||
# fixes bug #17913, but it's unclear why it's needed, maybe specific to MacOS Big Sur 11.3 on M1 arch?
|
||||
echo_run cp $nim_csources bin/nim
|
||||
echo_run $nim_csources -v
|
||||
)
|
||||
}
|
||||
59
ci/nsis_build.bat
Normal file
59
ci/nsis_build.bat
Normal file
@@ -0,0 +1,59 @@
|
||||
REM - Run the full testsuite; testament\tester all
|
||||
|
||||
REM - Uncomment the list of changes in news.txt
|
||||
REM - write a news ticker entry
|
||||
REM - Update the version
|
||||
|
||||
REM - Generate the full docs; koch web0
|
||||
REM - Generate the installers;
|
||||
REM - Update the version in system.nim
|
||||
REM - Test the installers
|
||||
REM - Tag the release
|
||||
REM - Merge devel into master
|
||||
REM - Update csources
|
||||
|
||||
set NIMVER=%1
|
||||
|
||||
Rem Build -docs file:
|
||||
koch web0
|
||||
cd web\upload
|
||||
7z a -tzip docs-%NIMVER%.zip *.html
|
||||
move /y docs-%NIMVER%.zip download
|
||||
cd ..\..
|
||||
|
||||
Rem Build csources
|
||||
koch csources -d:release || exit /b
|
||||
|
||||
rem Grab C sources and nimsuggest
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
|
||||
set PATH=%CD%\bin;%PATH%
|
||||
|
||||
ReM Build Win32 version:
|
||||
|
||||
set PATH=C:\Users\araq\projects\mingw32\bin;%PATH%
|
||||
cd csources
|
||||
call build.bat
|
||||
cd ..
|
||||
ReM Rebuilding koch is necessary because it uses its pointer size to determine
|
||||
ReM which mingw link to put in the NSIS installer.
|
||||
nim c --out:koch_temp koch || exit /b
|
||||
koch_temp boot -d:release || exit /b
|
||||
koch_temp nsis -d:release || exit /b
|
||||
koch_temp zip -d:release || exit /b
|
||||
dir build
|
||||
move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x32.exe || exit /b
|
||||
move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x32.zip || exit /b
|
||||
|
||||
|
||||
ReM Build Win64 version:
|
||||
set PATH=C:\Users\araq\projects\mingw64\bin;%PATH%
|
||||
cd csources
|
||||
call build64.bat
|
||||
cd ..
|
||||
nim c --out:koch_temp koch || exit /b
|
||||
koch_temp boot -d:release || exit /b
|
||||
koch_temp nsis -d:release || exit /b
|
||||
koch_temp zip -d:release || exit /b
|
||||
move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x64.exe || exit /b
|
||||
move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x64.zip || exit /b
|
||||
9
compiler.nimble
Normal file
9
compiler.nimble
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
version = system.NimVersion
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
|
||||
installDirs = @["compiler", "nimsuggest"]
|
||||
|
||||
requires "nim >= 0.14.0"
|
||||
@@ -22,17 +22,17 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
|
||||
result = arNo
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
result = isPartOfAux(n[i], b, marker)
|
||||
for i in 0 ..< len(n):
|
||||
result = isPartOfAux(n.sons[i], b, marker)
|
||||
if result == arYes: return
|
||||
of nkRecCase:
|
||||
assert(n[0].kind == nkSym)
|
||||
result = isPartOfAux(n[0], b, marker)
|
||||
assert(n.sons[0].kind == nkSym)
|
||||
result = isPartOfAux(n.sons[0], b, marker)
|
||||
if result == arYes: return
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
for i in 1 ..< len(n):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = isPartOfAux(lastSon(n[i]), b, marker)
|
||||
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
|
||||
if result == arYes: return
|
||||
else: discard "isPartOfAux(record case branch)"
|
||||
of nkSym:
|
||||
@@ -46,14 +46,14 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
|
||||
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
|
||||
case a.kind
|
||||
of tyObject:
|
||||
if a[0] != nil:
|
||||
result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker)
|
||||
if a.sons[0] != nil:
|
||||
result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker)
|
||||
if result == arNo: result = isPartOfAux(a.n, b, marker)
|
||||
of tyGenericInst, tyDistinct, tyAlias, tySink:
|
||||
result = isPartOfAux(lastSon(a), b, marker)
|
||||
of tyArray, tySet, tyTuple:
|
||||
for i in 0..<a.len:
|
||||
result = isPartOfAux(a[i], b, marker)
|
||||
for i in 0 ..< len(a):
|
||||
result = isPartOfAux(a.sons[i], b, marker)
|
||||
if result == arYes: return
|
||||
else: discard
|
||||
|
||||
@@ -108,7 +108,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
result = arMaybe
|
||||
of nkBracketExpr:
|
||||
result = isPartOf(a[0], b[0])
|
||||
if a.len >= 2 and b.len >= 2:
|
||||
if len(a) >= 2 and len(b) >= 2:
|
||||
# array accesses:
|
||||
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
|
||||
# we know it's the same array and we have 2 constant indexes;
|
||||
@@ -181,14 +181,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
else: discard
|
||||
of nkObjConstr:
|
||||
result = arNo
|
||||
for i in 1..<b.len:
|
||||
for i in 1 ..< b.len:
|
||||
let res = isPartOf(a, b[i][1])
|
||||
if res != arNo:
|
||||
result = res
|
||||
if res == arYes: break
|
||||
of nkCallKinds:
|
||||
result = arNo
|
||||
for i in 1..<b.len:
|
||||
for i in 1 ..< b.len:
|
||||
let res = isPartOf(a, b[i])
|
||||
if res != arNo:
|
||||
result = res
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#[
|
||||
move to std/asciitables.nim once stable, or to a fusion package
|
||||
once compiler can depend on fusion
|
||||
move to std/asciitables.nim once stable, or to a nimble paackage
|
||||
once compiler can depend on nimble
|
||||
]#
|
||||
|
||||
type Cell* = object
|
||||
@@ -8,7 +8,7 @@ type Cell* = object
|
||||
width*, row*, col*, ncols*, nrows*: int
|
||||
|
||||
iterator parseTableCells*(s: string, delim = '\t'): Cell =
|
||||
## Iterates over all cells in a `delim`-delimited `s`, after a 1st
|
||||
## iterates over all cells in a `delim`-delimited `s`, after a 1st
|
||||
## pass that computes number of rows, columns, and width of each column.
|
||||
var widths: seq[int]
|
||||
var cell: Cell
|
||||
@@ -69,7 +69,7 @@ iterator parseTableCells*(s: string, delim = '\t'): Cell =
|
||||
finishRow()
|
||||
|
||||
proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string =
|
||||
## Formats a `delim`-delimited `s` representing a table; each cell is aligned
|
||||
## formats a `delim`-delimited `s` representing a table; each cell is aligned
|
||||
## to a width that's computed for each column; consecutive columns are
|
||||
## delimited by `sep`, and alignment space is filled using `fill`.
|
||||
## More customized formatting can be done by calling `parseTableCells` directly.
|
||||
987
compiler/ast.nim
987
compiler/ast.nim
File diff suppressed because it is too large
Load Diff
@@ -24,55 +24,55 @@ proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int =
|
||||
proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
|
||||
|
||||
when declared(echo):
|
||||
# these are for debugging only: They are not really deprecated, but I want
|
||||
# the warning so that release versions do not contain debugging statements:
|
||||
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.}
|
||||
|
||||
# these are for debugging only: They are not really deprecated, but I want
|
||||
# the warning so that release versions do not contain debugging statements:
|
||||
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
|
||||
|
||||
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)
|
||||
elif compiles(c.graph.config):
|
||||
debug(c.graph.config, x)
|
||||
else:
|
||||
error()
|
||||
|
||||
template debug*(x: auto) {.deprecated.} =
|
||||
echo x
|
||||
|
||||
template mdbg*: bool {.deprecated.} =
|
||||
when compiles(c.graph):
|
||||
c.module.fileIdx == c.graph.config.projectMainIdx
|
||||
elif compiles(c.module):
|
||||
c.module.fileIdx == c.config.projectMainIdx
|
||||
elif compiles(c.c.module):
|
||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == m.c.config.projectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == cl.c.config.projectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == p.lex.config.projectMainIdx
|
||||
template debug*(x: PSym|PType|PNode) {.deprecated.} =
|
||||
when compiles(c.config):
|
||||
debug(c.config, x)
|
||||
elif compiles(c.graph.config):
|
||||
debug(c.graph.config, x)
|
||||
else:
|
||||
p.module.module.fileIdx == p.config.projectMainIdx
|
||||
elif compiles(m.module.fileIdx):
|
||||
m.module.fileIdx == m.config.projectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == L.config.projectMainIdx
|
||||
else:
|
||||
error()
|
||||
error()
|
||||
|
||||
template debug*(x: auto) {.deprecated.} =
|
||||
echo x
|
||||
|
||||
template mdbg*: bool {.deprecated.} =
|
||||
when compiles(c.graph):
|
||||
c.module.fileIdx == c.graph.config.projectMainIdx
|
||||
elif compiles(c.module):
|
||||
c.module.fileIdx == c.config.projectMainIdx
|
||||
elif compiles(c.c.module):
|
||||
c.c.module.fileIdx == c.c.config.projectMainIdx
|
||||
elif compiles(m.c.module):
|
||||
m.c.module.fileIdx == m.c.config.projectMainIdx
|
||||
elif compiles(cl.c.module):
|
||||
cl.c.module.fileIdx == cl.c.config.projectMainIdx
|
||||
elif compiles(p):
|
||||
when compiles(p.lex):
|
||||
p.lex.fileIdx == p.lex.config.projectMainIdx
|
||||
else:
|
||||
p.module.module.fileIdx == p.config.projectMainIdx
|
||||
elif compiles(m.module.fileIdx):
|
||||
m.module.fileIdx == m.config.projectMainIdx
|
||||
elif compiles(L.fileIdx):
|
||||
L.fileIdx == L.config.projectMainIdx
|
||||
else:
|
||||
error()
|
||||
|
||||
# --------------------------- ident tables ----------------------------------
|
||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||
@@ -110,15 +110,15 @@ proc iiTablePut*(t: var TIITable, key, val: int)
|
||||
|
||||
# implementation
|
||||
|
||||
proc skipConvCastAndClosure*(n: PNode): PNode =
|
||||
proc skipConvAndClosure*(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64,
|
||||
nkClosure:
|
||||
result = result[0]
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast:
|
||||
result = result[1]
|
||||
result = result.sons[0]
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = result.sons[1]
|
||||
else: break
|
||||
|
||||
proc sameValue*(a, b: PNode): bool =
|
||||
@@ -160,17 +160,17 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
result = lookupInRecord(n[i], field)
|
||||
for i in 0 ..< len(n):
|
||||
result = lookupInRecord(n.sons[i], field)
|
||||
if result != nil: return
|
||||
of nkRecCase:
|
||||
if (n[0].kind != nkSym): return nil
|
||||
result = lookupInRecord(n[0], field)
|
||||
if (n.sons[0].kind != nkSym): return nil
|
||||
result = lookupInRecord(n.sons[0], field)
|
||||
if result != nil: return
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
for i in 1 ..< len(n):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = lookupInRecord(lastSon(n[i]), field)
|
||||
result = lookupInRecord(lastSon(n.sons[i]), field)
|
||||
if result != nil: return
|
||||
else: return nil
|
||||
of nkSym:
|
||||
@@ -182,11 +182,10 @@ proc getModule*(s: PSym): PSym =
|
||||
assert((result.kind == skModule) or (result.owner != result))
|
||||
while result != nil and result.kind != skModule: result = result.owner
|
||||
|
||||
proc fromSystem*(op: PSym): bool {.inline.} = sfSystemModule in getModule(op).flags
|
||||
proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
|
||||
for i in start..<list.len:
|
||||
if list[i].kind == nkSym:
|
||||
result = list[i].sym
|
||||
for i in start ..< len(list):
|
||||
if list.sons[i].kind == nkSym:
|
||||
result = list.sons[i].sym
|
||||
if result.name.id == ident.id: return
|
||||
else: return nil
|
||||
result = nil
|
||||
@@ -219,14 +218,14 @@ proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
|
||||
|
||||
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
|
||||
## gensym'ed and then they have '`<number>' suffix that we need to
|
||||
## ignore, see compiler / evaltempl.nim, snippet:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
|
||||
## result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id),
|
||||
## if c.instLines: actual.info else: templ.info)
|
||||
for i in 1..<list.len:
|
||||
for i in 1 ..< len(list):
|
||||
let it = list[i].sym
|
||||
if it.name.id == ident.id or
|
||||
sameIgnoreBacktickGensymInfo(it.name.s, ident.s): return it
|
||||
@@ -255,15 +254,15 @@ proc makeYamlString*(s: string): Rope =
|
||||
const MaxLineLength = 64
|
||||
result = nil
|
||||
var res = "\""
|
||||
for i in 0..<s.len:
|
||||
for i in 0 ..< s.len:
|
||||
if (i + 1) mod MaxLineLength == 0:
|
||||
res.add('\"')
|
||||
res.add("\n")
|
||||
result.add(rope(res))
|
||||
add(res, '\"')
|
||||
add(res, "\n")
|
||||
add(result, rope(res))
|
||||
res = "\"" # reset
|
||||
res.add(toYamlChar(s[i]))
|
||||
res.add('\"')
|
||||
result.add(rope(res))
|
||||
add(res, toYamlChar(s[i]))
|
||||
add(res, '\"')
|
||||
add(result, rope(res))
|
||||
|
||||
proc flagsToStr[T](flags: set[T]): Rope =
|
||||
if flags == {}:
|
||||
@@ -271,8 +270,8 @@ proc flagsToStr[T](flags: set[T]): Rope =
|
||||
else:
|
||||
result = nil
|
||||
for x in items(flags):
|
||||
if result != nil: result.add(", ")
|
||||
result.add(makeYamlString($x))
|
||||
if result != nil: add(result, ", ")
|
||||
add(result, makeYamlString($x))
|
||||
result = "[" & result & "]"
|
||||
|
||||
proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
|
||||
@@ -299,25 +298,25 @@ proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
|
||||
# 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)])
|
||||
addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
|
||||
addf(result, "$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
|
||||
addf(result, "$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
|
||||
if conf != nil:
|
||||
# if we don't pass the config, we probably don't care about the line info
|
||||
result.addf("$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
addf(result, "$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)])
|
||||
addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
|
||||
addf(result, "$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
|
||||
addf(result, "$N$1\"ast\": $2", [istr, ast])
|
||||
addf(result, "$N$1\"options\": $2", [istr, flagsToStr(n.options)])
|
||||
addf(result, "$N$1\"position\": $2", [istr, rope(n.position)])
|
||||
addf(result, "$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
|
||||
addf(result, "$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
|
||||
if card(n.loc.flags) > 0:
|
||||
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)])
|
||||
addf(result, "$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
|
||||
addf(result, "$N$1\"r\": $2", [istr, n.loc.r])
|
||||
addf(result, "$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): Rope =
|
||||
@@ -328,27 +327,27 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
|
||||
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
|
||||
else:
|
||||
if n.len > 0:
|
||||
if len(n) > 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],
|
||||
for i in 0 ..< len(n):
|
||||
if i > 0: add(sonsRope, ",")
|
||||
addf(sonsRope, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i],
|
||||
marker, indent + 4, maxRecDepth - 1)])
|
||||
sonsRope.addf("$N$1]", [rspaces(indent + 2)])
|
||||
addf(sonsRope, "$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)])
|
||||
addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
|
||||
addf(result, "$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
|
||||
addf(result, "$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
|
||||
if card(n.flags) > 0:
|
||||
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])
|
||||
addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
|
||||
addf(result, "$N$1\"callconv\": $2", [istr, makeYamlString(CallingConvToStr[n.callConv])])
|
||||
addf(result, "$N$1\"size\": $2", [istr, rope(n.size)])
|
||||
addf(result, "$N$1\"align\": $2", [istr, rope(n.align)])
|
||||
addf(result, "$N$1\"sons\": $2", [istr, sonsRope])
|
||||
|
||||
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): Rope =
|
||||
@@ -359,34 +358,34 @@ proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
|
||||
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)])
|
||||
addf(result, ",$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)])
|
||||
addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
|
||||
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
result.addf(",$N$1\"floatVal\": $2",
|
||||
addf(result, ",$N$1\"floatVal\": $2",
|
||||
[istr, rope(n.floatVal.toStrMaxPrecision)])
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
of nkSym:
|
||||
result.addf(",$N$1\"sym\": $2",
|
||||
addf(result, ",$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)])
|
||||
addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
|
||||
else:
|
||||
result.addf(",$N$1\"ident\": null", [istr])
|
||||
addf(result, ",$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],
|
||||
if len(n) > 0:
|
||||
addf(result, ",$N$1\"sons\": [", [istr])
|
||||
for i in 0 ..< len(n):
|
||||
if i > 0: add(result, ",")
|
||||
addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i],
|
||||
marker, indent + 4, maxRecDepth - 1)])
|
||||
result.addf("$N$1]", [istr])
|
||||
result.addf(",$N$1\"typ\": $2",
|
||||
addf(result, "$N$1]", [istr])
|
||||
addf(result, ",$N$1\"typ\": $2",
|
||||
[istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
|
||||
result.addf("$N$1}", [rspaces(indent)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
@@ -428,7 +427,7 @@ proc indentLess(this: var DebugPrinter) =
|
||||
proc newlineAndIndent(this: var DebugPrinter) =
|
||||
this.res.add "\n"
|
||||
this.currentLine += 1
|
||||
for i in 0..<this.indent:
|
||||
for i in 0 ..< this.indent:
|
||||
this.res.add ' '
|
||||
|
||||
proc openCurly(this: var DebugPrinter) =
|
||||
@@ -564,12 +563,12 @@ proc value(this: var DebugPrinter; value: PType) =
|
||||
this.key "n"
|
||||
this.value value.n
|
||||
|
||||
if value.len > 0:
|
||||
if len(value) > 0:
|
||||
this.key "sons"
|
||||
this.openBracket
|
||||
for i in 0..<value.len:
|
||||
this.value value[i]
|
||||
if i != value.len - 1:
|
||||
for i in 0 ..< len(value):
|
||||
this.value value.sons[i]
|
||||
if i != len(value) - 1:
|
||||
this.comma
|
||||
this.closeBracket
|
||||
|
||||
@@ -585,26 +584,16 @@ proc value(this: var DebugPrinter; value: PNode) =
|
||||
this.openCurly
|
||||
this.key "kind"
|
||||
this.value value.kind
|
||||
if value.comment.len > 0:
|
||||
this.key "comment"
|
||||
this.value value.comment
|
||||
when defined(useNodeIds):
|
||||
this.key "id"
|
||||
this.value value.id
|
||||
if this.conf != nil:
|
||||
this.key "info"
|
||||
this.value $lineInfoToStr(this.conf, value.info)
|
||||
if value.flags != {}:
|
||||
if card(value.flags) > 0:
|
||||
this.key "flags"
|
||||
this.value value.flags
|
||||
|
||||
if value.typ != nil:
|
||||
this.key "typ"
|
||||
this.value value.typ.kind
|
||||
else:
|
||||
this.key "typ"
|
||||
this.value "nil"
|
||||
|
||||
case value.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
this.key "intVal"
|
||||
@@ -627,41 +616,41 @@ proc value(this: var DebugPrinter; value: PNode) =
|
||||
if this.renderSymType and value.typ != nil:
|
||||
this.key "typ"
|
||||
this.value value.typ
|
||||
if value.len > 0:
|
||||
if len(value) > 0:
|
||||
this.key "sons"
|
||||
this.openBracket
|
||||
for i in 0..<value.len:
|
||||
this.value value[i]
|
||||
if i != value.len - 1:
|
||||
for i in 0 ..< len(value):
|
||||
this.value value.sons[i]
|
||||
if i != len(value) - 1:
|
||||
this.comma
|
||||
this.closeBracket
|
||||
|
||||
this.closeCurly
|
||||
|
||||
when declared(echo):
|
||||
proc debug(n: PSym; conf: ConfigRef) =
|
||||
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: PSym; conf: ConfigRef) =
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -688,13 +677,13 @@ proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
|
||||
|
||||
proc objectSetEnlarge(t: var TObjectSet) =
|
||||
var n: TObjectSeq
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(t.data):
|
||||
newSeq(n, len(t.data) * GrowthFactor)
|
||||
for i in 0 .. high(t.data):
|
||||
if t.data[i] != nil: objectSetRawInsert(n, t.data[i])
|
||||
swap(t.data, n)
|
||||
|
||||
proc objectSetIncl*(t: var TObjectSet, obj: RootRef) =
|
||||
if mustRehash(t.data.len, t.counter): objectSetEnlarge(t)
|
||||
if mustRehash(len(t.data), t.counter): objectSetEnlarge(t)
|
||||
objectSetRawInsert(t.data, obj)
|
||||
inc(t.counter)
|
||||
|
||||
@@ -707,7 +696,7 @@ proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool =
|
||||
if it == obj:
|
||||
return true # found it
|
||||
h = nextTry(h, high(t.data))
|
||||
if mustRehash(t.data.len, t.counter):
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
objectSetEnlarge(t)
|
||||
objectSetRawInsert(t.data, obj)
|
||||
else:
|
||||
@@ -750,21 +739,21 @@ proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) =
|
||||
|
||||
proc strTableEnlarge(t: var TStrTable) =
|
||||
var n: seq[PSym]
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(t.data):
|
||||
newSeq(n, len(t.data) * 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)
|
||||
if mustRehash(len(t.data), 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.
|
||||
# returns true if n is already in the string table:
|
||||
# It is essential that `n` is written nevertheless!
|
||||
# This way the newest redefinition is picked by the semantic analyses!
|
||||
assert n.name != nil
|
||||
var h: Hash = n.name.h and high(t.data)
|
||||
var replaceSlot = -1
|
||||
@@ -780,11 +769,10 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
|
||||
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):
|
||||
return t.data[replaceSlot] # found it
|
||||
elif mustRehash(len(t.data), t.counter):
|
||||
strTableEnlarge(t)
|
||||
strTableRawInsert(t.data, n)
|
||||
else:
|
||||
@@ -888,7 +876,7 @@ iterator items*(tab: TStrTable): PSym =
|
||||
s = nextIter(it, tab)
|
||||
|
||||
proc hasEmptySlot(data: TIdPairSeq): bool =
|
||||
for h in 0..high(data):
|
||||
for h in 0 .. high(data):
|
||||
if data[h].key == nil:
|
||||
return true
|
||||
result = false
|
||||
@@ -941,9 +929,9 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
|
||||
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 mustRehash(len(t.data), t.counter):
|
||||
newSeq(n, len(t.data) * GrowthFactor)
|
||||
for i in 0 .. high(t.data):
|
||||
if t.data[i].key != nil:
|
||||
idTableRawInsert(n, t.data[i].key, t.data[i].val)
|
||||
assert(hasEmptySlot(n))
|
||||
@@ -952,7 +940,7 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
|
||||
inc(t.counter)
|
||||
|
||||
iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
|
||||
for i in 0..high(t.data):
|
||||
for i in 0 .. high(t.data):
|
||||
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
|
||||
@@ -986,10 +974,10 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
|
||||
assert(t.data[index].key != nil)
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(t.data.len, t.counter):
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
var n: TIdNodePairSeq
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(t.data):
|
||||
newSeq(n, len(t.data) * 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)
|
||||
@@ -997,13 +985,13 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
|
||||
inc(t.counter)
|
||||
|
||||
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
|
||||
for i in 0..high(t.data):
|
||||
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
|
||||
newSeq(x.data, StartSize)
|
||||
for i in 0..<StartSize: x.data[i].key = InvalidKey
|
||||
for i in 0 ..< StartSize: x.data[i].key = InvalidKey
|
||||
|
||||
proc iiTableRawGet(t: TIITable, key: int): int =
|
||||
var h: Hash
|
||||
@@ -1034,11 +1022,11 @@ proc iiTablePut(t: var TIITable, key, val: int) =
|
||||
assert(t.data[index].key != InvalidKey)
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(t.data.len, t.counter):
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
var n: TIIPairSeq
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(n): n[i].key = InvalidKey
|
||||
for i in 0..high(t.data):
|
||||
newSeq(n, len(t.data) * GrowthFactor)
|
||||
for i in 0 .. high(n): n[i].key = InvalidKey
|
||||
for i in 0 .. high(t.data):
|
||||
if t.data[i].key != InvalidKey:
|
||||
iiTableRawInsert(n, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
@@ -1052,14 +1040,3 @@ proc isAddrNode*(n: PNode): bool =
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
|
||||
else: false
|
||||
else: false
|
||||
|
||||
proc listSymbolNames*(symbols: openArray[PSym]): string =
|
||||
for sym in symbols:
|
||||
if result.len > 0:
|
||||
result.add ", "
|
||||
result.add sym.name.s
|
||||
|
||||
proc isDiscriminantField*(n: PNode): bool =
|
||||
if n.kind == nkCheckedFieldExpr: sfDiscriminant in n[0][1].sym.flags
|
||||
elif n.kind == nkDotExpr: sfDiscriminant in n[1].sym.flags
|
||||
else: false
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# this module avoids ast depending on msgs or vice versa
|
||||
import std/strutils
|
||||
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[0].sym
|
||||
|
||||
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
|
||||
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
|
||||
|
||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) =
|
||||
if optDeclaredLocs in conf.globalOptions and sym != nil:
|
||||
addDeclaredLoc(result, conf, sym)
|
||||
|
||||
proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
# xxx figure out how to resolve `tyGenericParam`, e.g. for
|
||||
# proc fn[T](a: T, b: T) = discard
|
||||
# fn(1.1, "a")
|
||||
let typ = typ.skipTypes(abstractInst + {tyStatic, tySequence, tyArray, tySet, tyUserTypeClassInst, tyVar, tyRef, tyPtr} - {tyRange})
|
||||
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)
|
||||
|
||||
template quoteExpr*(a: string): untyped =
|
||||
## can be used for quoting expressions in error msgs.
|
||||
"'" & a & "'"
|
||||
|
||||
proc genFieldDefect*(conf: ConfigRef, field: string, disc: PSym): string =
|
||||
let obj = disc.owner.name.s # `types.typeToString` might be better, eg for generics
|
||||
result = "field '$#' is not accessible for type '$#'" % [field, obj]
|
||||
if optDeclaredLocs in conf.globalOptions:
|
||||
result.add " [discriminant declared in $#]" % toFileLineCol(conf, disc.info)
|
||||
result.add " using '$# = " % disc.name.s
|
||||
@@ -52,25 +52,25 @@ proc bitSetInit(b: var TBitSet, length: int) =
|
||||
newSeq(b, length)
|
||||
|
||||
proc bitSetUnion(x: var TBitSet, y: TBitSet) =
|
||||
for i in 0..high(x): x[i] = x[i] or y[i]
|
||||
for i in 0 .. high(x): x[i] = x[i] or y[i]
|
||||
|
||||
proc bitSetDiff(x: var TBitSet, y: TBitSet) =
|
||||
for i in 0..high(x): x[i] = x[i] and not y[i]
|
||||
for i in 0 .. high(x): x[i] = x[i] and not y[i]
|
||||
|
||||
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
|
||||
for i in 0..high(x): x[i] = x[i] xor y[i]
|
||||
for i in 0 .. high(x): x[i] = x[i] xor y[i]
|
||||
|
||||
proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
|
||||
for i in 0..high(x): x[i] = x[i] and y[i]
|
||||
for i in 0 .. high(x): x[i] = x[i] and y[i]
|
||||
|
||||
proc bitSetEquals(x, y: TBitSet): bool =
|
||||
for i in 0..high(x):
|
||||
for i in 0 .. high(x):
|
||||
if x[i] != y[i]:
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc bitSetContains(x, y: TBitSet): bool =
|
||||
for i in 0..high(x):
|
||||
for i in 0 .. high(x):
|
||||
if (x[i] and not y[i]) != Zero:
|
||||
return false
|
||||
result = true
|
||||
|
||||
@@ -16,7 +16,7 @@ const
|
||||
Mhalf = M div 2
|
||||
|
||||
type
|
||||
Node[Key, Val] {.acyclic.} = ref object
|
||||
Node[Key, Val] = ref object
|
||||
entries: int
|
||||
keys: array[M, Key]
|
||||
case isInternal: bool
|
||||
@@ -37,34 +37,34 @@ template eq(a, b): bool = cmp(a, b) == 0
|
||||
proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
|
||||
var x = b.root
|
||||
while x.isInternal:
|
||||
for j in 0..<x.entries:
|
||||
for j in 0 ..< x.entries:
|
||||
if j+1 == x.entries or less(key, x.keys[j+1]):
|
||||
x = x.links[j]
|
||||
break
|
||||
assert(not x.isInternal)
|
||||
for j in 0..<x.entries:
|
||||
for j in 0 ..< x.entries:
|
||||
if eq(key, x.keys[j]): return x.vals[j]
|
||||
|
||||
proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
|
||||
var x = b.root
|
||||
while x.isInternal:
|
||||
for j in 0..<x.entries:
|
||||
for j in 0 ..< x.entries:
|
||||
if j+1 == x.entries or less(key, x.keys[j+1]):
|
||||
x = x.links[j]
|
||||
break
|
||||
assert(not x.isInternal)
|
||||
for j in 0..<x.entries:
|
||||
for j in 0 ..< x.entries:
|
||||
if eq(key, x.keys[j]): return true
|
||||
return false
|
||||
|
||||
proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
|
||||
for j in 0..<Mhalf:
|
||||
for j in 0 ..< Mhalf:
|
||||
result.keys[j] = h.keys[Mhalf + j]
|
||||
if h.isInternal:
|
||||
for j in 0..<Mhalf:
|
||||
for j in 0 ..< Mhalf:
|
||||
result.links[j] = h.links[Mhalf + j]
|
||||
else:
|
||||
for j in 0..<Mhalf:
|
||||
for j in 0 ..< Mhalf:
|
||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||
|
||||
proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
|
||||
@@ -79,9 +79,6 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
|
||||
var j = 0
|
||||
if not h.isInternal:
|
||||
while j < h.entries:
|
||||
if eq(key, h.keys[j]):
|
||||
h.vals[j] = val
|
||||
return
|
||||
if less(key, h.keys[j]): break
|
||||
inc j
|
||||
for i in countdown(h.entries, j+1):
|
||||
@@ -135,7 +132,8 @@ proc `$`[Key, Val](b: BTree[Key, Val]): string =
|
||||
result = ""
|
||||
toString(b.root, "", result)
|
||||
|
||||
proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = index < b.entries
|
||||
proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool =
|
||||
result = index < b.entries
|
||||
|
||||
proc countSubTree[Key, Val](it: Node[Key, Val]): int =
|
||||
if it.isInternal:
|
||||
@@ -168,3 +166,68 @@ iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) =
|
||||
yield (k, v)
|
||||
|
||||
proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries
|
||||
|
||||
when isMainModule:
|
||||
|
||||
import random, tables
|
||||
|
||||
proc main =
|
||||
var st = initBTree[string, string]()
|
||||
st.add("www.cs.princeton.edu", "abc")
|
||||
st.add("www.princeton.edu", "128.112.128.15")
|
||||
st.add("www.yale.edu", "130.132.143.21")
|
||||
st.add("www.simpsons.com", "209.052.165.60")
|
||||
st.add("www.apple.com", "17.112.152.32")
|
||||
st.add("www.amazon.com", "207.171.182.16")
|
||||
st.add("www.ebay.com", "66.135.192.87")
|
||||
st.add("www.cnn.com", "64.236.16.20")
|
||||
st.add("www.google.com", "216.239.41.99")
|
||||
st.add("www.nytimes.com", "199.239.136.200")
|
||||
st.add("www.microsoft.com", "207.126.99.140")
|
||||
st.add("www.dell.com", "143.166.224.230")
|
||||
st.add("www.slashdot.org", "66.35.250.151")
|
||||
st.add("www.espn.com", "199.181.135.201")
|
||||
st.add("www.weather.com", "63.111.66.11")
|
||||
st.add("www.yahoo.com", "216.109.118.65")
|
||||
|
||||
assert st.getOrDefault("www.cs.princeton.edu") == "abc"
|
||||
assert st.getOrDefault("www.harvardsucks.com") == nil
|
||||
|
||||
assert st.getOrDefault("www.simpsons.com") == "209.052.165.60"
|
||||
assert st.getOrDefault("www.apple.com") == "17.112.152.32"
|
||||
assert st.getOrDefault("www.ebay.com") == "66.135.192.87"
|
||||
assert st.getOrDefault("www.dell.com") == "143.166.224.230"
|
||||
assert(st.entries == 16)
|
||||
|
||||
for k, v in st:
|
||||
echo k, ": ", v
|
||||
|
||||
when false:
|
||||
var b2 = initBTree[string, string]()
|
||||
const iters = 10_000
|
||||
for i in 1..iters:
|
||||
b2.add($i, $(iters - i))
|
||||
for i in 1..iters:
|
||||
let x = b2.getOrDefault($i)
|
||||
if x != $(iters - i):
|
||||
echo "got ", x, ", but expected ", iters - i
|
||||
echo b2.entries
|
||||
|
||||
when true:
|
||||
var b2 = initBTree[int, string]()
|
||||
var t2 = initTable[int, string]()
|
||||
const iters = 100_000
|
||||
for i in 1..iters:
|
||||
let x = rand(high(int))
|
||||
if not t2.hasKey(x):
|
||||
doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x
|
||||
t2[x] = $x
|
||||
b2.add(x, $x)
|
||||
|
||||
doAssert b2.entries == t2.len
|
||||
echo "unique entries ", b2.entries
|
||||
for k, v in t2:
|
||||
doAssert $k == v
|
||||
doAssert b2.getOrDefault(k) == $k
|
||||
|
||||
main()
|
||||
|
||||
421
compiler/canonicalizer.nim
Normal file
421
compiler/canonicalizer.nim
Normal file
@@ -0,0 +1,421 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the canonalization for the various caching mechanisms.
|
||||
|
||||
import strutils, db_sqlite, md5
|
||||
|
||||
var db: DbConn
|
||||
|
||||
# We *hash* the relevant information into 128 bit hashes. This should be good
|
||||
# enough to prevent any collisions.
|
||||
|
||||
type
|
||||
TUid = distinct MD5Digest
|
||||
|
||||
# For name mangling we encode these hashes via a variant of base64 (called
|
||||
# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
|
||||
# So a signature like:
|
||||
#
|
||||
# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
|
||||
#
|
||||
# is mangled into:
|
||||
# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
|
||||
#
|
||||
# This is a good compromise between correctness and brevity. ;-)
|
||||
|
||||
const
|
||||
cb64 = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
|
||||
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"_A", "_B"]
|
||||
|
||||
proc toBase64a(s: cstring, len: int): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||
var i = 0
|
||||
while i < s.len - 2:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
let c = ord(s[i+2])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result.add cb64[c and 0x3F]
|
||||
inc(i, 3)
|
||||
if i < s.len-1:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result.add cb64[((b and 0x0F) shl 2)]
|
||||
elif i < s.len:
|
||||
let a = ord(s[i])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[(a and 3) shl 4]
|
||||
|
||||
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
||||
|
||||
proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
|
||||
|
||||
proc hashSym(c: var MD5Context, s: PSym) =
|
||||
if sfAnon in s.flags or s.kind == skGenericParam:
|
||||
c &= ":anon"
|
||||
else:
|
||||
var it = s.owner
|
||||
while it != nil:
|
||||
hashSym(c, it)
|
||||
c &= "."
|
||||
it = s.owner
|
||||
c &= s.name.s
|
||||
|
||||
proc hashTree(c: var MD5Context, n: PNode) =
|
||||
if n == nil:
|
||||
c &= "\255"
|
||||
return
|
||||
var k = n.kind
|
||||
md5Update(c, cast[cstring](addr(k)), 1)
|
||||
# we really must not hash line information. 'n.typ' is debatable but
|
||||
# shouldn't be necessary for now and avoids potential infinite recursions.
|
||||
case n.kind
|
||||
of nkEmpty, nkNilLit, nkType: discard
|
||||
of nkIdent:
|
||||
c &= n.ident.s
|
||||
of nkSym:
|
||||
hashSym(c, n.sym)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
var v = n.intVal
|
||||
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
var v = n.floatVal
|
||||
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
c &= n.strVal
|
||||
else:
|
||||
for i in 0..<n.len: hashTree(c, n.sons[i])
|
||||
|
||||
proc hashType(c: var MD5Context, t: PType) =
|
||||
# modelled after 'typeToString'
|
||||
if t == nil:
|
||||
c &= "\254"
|
||||
return
|
||||
|
||||
var k = t.kind
|
||||
md5Update(c, cast[cstring](addr(k)), 1)
|
||||
|
||||
if t.sym != nil and sfAnon notin t.sym.flags:
|
||||
# t.n for literals, but not for e.g. objects!
|
||||
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
||||
c.hashSym(t.sym)
|
||||
|
||||
case t.kind
|
||||
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
||||
for i in 0 ..< len(t)-ord(t.kind != tyGenericInvocation):
|
||||
c.hashType t.sons[i]
|
||||
of tyUserTypeClass:
|
||||
internalAssert t.sym != nil and t.sym.owner != nil
|
||||
c &= t.sym.owner.name.s
|
||||
of tyUserTypeClassInst:
|
||||
let body = t.base
|
||||
c.hashSym body.sym
|
||||
for i in 1 .. len(t) - 2:
|
||||
c.hashType t.sons[i]
|
||||
of tyFromExpr:
|
||||
c.hashTree(t.n)
|
||||
of tyArray:
|
||||
c.hashTree(t.sons[0].n)
|
||||
c.hashType(t.sons[1])
|
||||
of tyTuple:
|
||||
if t.n != nil:
|
||||
assert(len(t.n) == len(t))
|
||||
for i in 0 ..< len(t.n):
|
||||
assert(t.n.sons[i].kind == nkSym)
|
||||
c &= t.n.sons[i].sym.name.s
|
||||
c &= ":"
|
||||
c.hashType(t.sons[i])
|
||||
c &= ","
|
||||
else:
|
||||
for i in 0 ..< len(t): c.hashType t.sons[i]
|
||||
of tyRange:
|
||||
c.hashTree(t.n)
|
||||
c.hashType(t.sons[0])
|
||||
of tyProc:
|
||||
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
|
||||
for i in 0..<t.len: c.hashType(t.sons[i])
|
||||
md5Update(c, cast[cstring](addr(t.callConv)), 1)
|
||||
|
||||
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
|
||||
if tfThread in t.flags: c &= ".thread"
|
||||
else:
|
||||
for i in 0..<t.len: c.hashType(t.sons[i])
|
||||
if tfNotNil in t.flags: c &= "not nil"
|
||||
|
||||
proc canonConst(n: PNode): TUid =
|
||||
var c: MD5Context
|
||||
md5Init(c)
|
||||
c.hashTree(n)
|
||||
c.hashType(n.typ)
|
||||
md5Final(c, MD5Digest(result))
|
||||
|
||||
proc canonSym(s: PSym): TUid =
|
||||
var c: MD5Context
|
||||
md5Init(c)
|
||||
c.hashSym(s)
|
||||
md5Final(c, MD5Digest(result))
|
||||
|
||||
proc pushType(w: PRodWriter, t: PType) =
|
||||
# check so that the stack does not grow too large:
|
||||
if iiTableGet(w.index.tab, t.id) == InvalidKey:
|
||||
w.tstack.add(t)
|
||||
|
||||
proc pushSym(w: PRodWriter, s: PSym) =
|
||||
# check so that the stack does not grow too large:
|
||||
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
||||
w.sstack.add(s)
|
||||
|
||||
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
result: var string) =
|
||||
if n == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("()")
|
||||
return
|
||||
result.add('(')
|
||||
encodeVInt(ord(n.kind), result)
|
||||
# we do not write comments for now
|
||||
# Line information takes easily 20% or more of the filesize! Therefore we
|
||||
# omit line information if it is the same as the father's line information:
|
||||
if fInfo.fileIndex != n.info.fileIndex:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(n.info.line, result)
|
||||
result.add(',')
|
||||
encodeVInt(fileIdx(w, toFilename(n.info)), result)
|
||||
elif fInfo.line != n.info.line:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(n.info.line, result)
|
||||
elif fInfo.col != n.info.col:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
var f = n.flags * PersistentNodeFlags
|
||||
if f != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](f), result)
|
||||
if n.typ != nil:
|
||||
result.add('^')
|
||||
encodeVInt(n.typ.id, result)
|
||||
pushType(w, n.typ)
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
if n.intVal != 0:
|
||||
result.add('!')
|
||||
encodeVBiggestInt(n.intVal, result)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if n.floatVal != 0.0:
|
||||
result.add('!')
|
||||
encodeStr($n.floatVal, result)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if n.strVal != "":
|
||||
result.add('!')
|
||||
encodeStr(n.strVal, result)
|
||||
of nkIdent:
|
||||
result.add('!')
|
||||
encodeStr(n.ident.s, result)
|
||||
of nkSym:
|
||||
result.add('!')
|
||||
encodeVInt(n.sym.id, result)
|
||||
pushSym(w, n.sym)
|
||||
else:
|
||||
for i in 0 ..< len(n):
|
||||
encodeNode(w, n.info, n.sons[i], result)
|
||||
add(result, ')')
|
||||
|
||||
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||
var oldLen = result.len
|
||||
result.add('<')
|
||||
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
|
||||
if loc.s != low(loc.s):
|
||||
add(result, '*')
|
||||
encodeVInt(ord(loc.s), result)
|
||||
if loc.flags != {}:
|
||||
add(result, '$')
|
||||
encodeVInt(cast[int32](loc.flags), result)
|
||||
if loc.t != nil:
|
||||
add(result, '^')
|
||||
encodeVInt(cast[int32](loc.t.id), result)
|
||||
pushType(w, loc.t)
|
||||
if loc.r != nil:
|
||||
add(result, '!')
|
||||
encodeStr($loc.r, result)
|
||||
if loc.a != 0:
|
||||
add(result, '?')
|
||||
encodeVInt(loc.a, result)
|
||||
if oldLen + 1 == result.len:
|
||||
# no data was necessary, so remove the '<' again:
|
||||
setLen(result, oldLen)
|
||||
else:
|
||||
add(result, '>')
|
||||
|
||||
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||
if t == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("[]")
|
||||
return
|
||||
# we need no surrounding [] here because the type is in a line of its own
|
||||
if t.kind == tyForward: internalError("encodeType: tyForward")
|
||||
# for the new rodfile viewer we use a preceding [ so that the data section
|
||||
# can easily be disambiguated:
|
||||
add(result, '[')
|
||||
encodeVInt(ord(t.kind), result)
|
||||
add(result, '+')
|
||||
encodeVInt(t.id, result)
|
||||
if t.n != nil:
|
||||
encodeNode(w, unknownLineInfo(), t.n, result)
|
||||
if t.flags != {}:
|
||||
add(result, '$')
|
||||
encodeVInt(cast[int32](t.flags), result)
|
||||
if t.callConv != low(t.callConv):
|
||||
add(result, '?')
|
||||
encodeVInt(ord(t.callConv), result)
|
||||
if t.owner != nil:
|
||||
add(result, '*')
|
||||
encodeVInt(t.owner.id, result)
|
||||
pushSym(w, t.owner)
|
||||
if t.sym != nil:
|
||||
add(result, '&')
|
||||
encodeVInt(t.sym.id, result)
|
||||
pushSym(w, t.sym)
|
||||
if t.size != - 1:
|
||||
add(result, '/')
|
||||
encodeVBiggestInt(t.size, result)
|
||||
if t.align != - 1:
|
||||
add(result, '=')
|
||||
encodeVInt(t.align, result)
|
||||
encodeLoc(w, t.loc, result)
|
||||
for i in 0 ..< len(t):
|
||||
if t.sons[i] == nil:
|
||||
add(result, "^()")
|
||||
else:
|
||||
add(result, '^')
|
||||
encodeVInt(t.sons[i].id, result)
|
||||
pushType(w, t.sons[i])
|
||||
|
||||
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
||||
add(result, '|')
|
||||
encodeVInt(ord(lib.kind), result)
|
||||
add(result, '|')
|
||||
encodeStr($lib.name, result)
|
||||
add(result, '|')
|
||||
encodeNode(w, info, lib.path, result)
|
||||
|
||||
proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
|
||||
if s == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("{}")
|
||||
return
|
||||
# we need no surrounding {} here because the symbol is in a line of its own
|
||||
encodeVInt(ord(s.kind), result)
|
||||
result.add('+')
|
||||
encodeVInt(s.id, result)
|
||||
result.add('&')
|
||||
encodeStr(s.name.s, result)
|
||||
if s.typ != nil:
|
||||
result.add('^')
|
||||
encodeVInt(s.typ.id, result)
|
||||
pushType(w, s.typ)
|
||||
result.add('?')
|
||||
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
|
||||
result.add(',')
|
||||
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
|
||||
result.add(',')
|
||||
encodeVInt(fileIdx(w, toFilename(s.info)), result)
|
||||
if s.owner != nil:
|
||||
result.add('*')
|
||||
encodeVInt(s.owner.id, result)
|
||||
pushSym(w, s.owner)
|
||||
if s.flags != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](s.flags), result)
|
||||
if s.magic != mNone:
|
||||
result.add('@')
|
||||
encodeVInt(ord(s.magic), result)
|
||||
if s.options != w.options:
|
||||
result.add('!')
|
||||
encodeVInt(cast[int32](s.options), result)
|
||||
if s.position != 0:
|
||||
result.add('%')
|
||||
encodeVInt(s.position, result)
|
||||
if s.offset != - 1:
|
||||
result.add('`')
|
||||
encodeVInt(s.offset, result)
|
||||
encodeLoc(w, s.loc, result)
|
||||
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
|
||||
if s.constraint != nil:
|
||||
add(result, '#')
|
||||
encodeNode(w, unknownLineInfo(), s.constraint, result)
|
||||
# lazy loading will soon reload the ast lazily, so the ast needs to be
|
||||
# the last entry of a symbol:
|
||||
if s.ast != nil:
|
||||
# we used to attempt to save space here by only storing a dummy AST if
|
||||
# it is not necessary, but Nim's heavy compile-time evaluation features
|
||||
# make that unfeasible nowadays:
|
||||
encodeNode(w, s.info, s.ast, result)
|
||||
|
||||
|
||||
proc createDb() =
|
||||
db.exec(sql"""
|
||||
create table if not exists Module(
|
||||
id integer primary key,
|
||||
name varchar(256) not null,
|
||||
fullpath varchar(256) not null,
|
||||
interfHash varchar(256) not null,
|
||||
fullHash varchar(256) not null,
|
||||
|
||||
created timestamp not null default (DATETIME('now'))
|
||||
);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists Backend(
|
||||
id integer primary key,
|
||||
strongdeps varchar(max) not null,
|
||||
weakdeps varchar(max) not null,
|
||||
header varchar(max) not null,
|
||||
code varchar(max) not null
|
||||
)
|
||||
|
||||
create table if not exists Symbol(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
backend integer not null,
|
||||
name varchar(max) not null,
|
||||
data varchar(max) not null,
|
||||
created timestamp not null default (DATETIME('now')),
|
||||
|
||||
foreign key (module) references Module(id),
|
||||
foreign key (backend) references Backend(id)
|
||||
);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists Type(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
name varchar(max) not null,
|
||||
data varchar(max) not null,
|
||||
created timestamp not null default (DATETIME('now')),
|
||||
|
||||
foreign key (module) references module(id)
|
||||
);""")
|
||||
|
||||
|
||||
@@ -9,100 +9,43 @@
|
||||
#
|
||||
# included from cgen.nim
|
||||
|
||||
proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
# we assume things like sysFatal cannot raise themselves
|
||||
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
|
||||
result = false
|
||||
elif optPanics in p.config.globalOptions or
|
||||
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags):
|
||||
# we know we can be strict:
|
||||
result = canRaise(n)
|
||||
else:
|
||||
# we have to be *very* conservative:
|
||||
result = canRaiseConservative(n)
|
||||
|
||||
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
||||
var n = le
|
||||
while true:
|
||||
# do NOT follow nkHiddenDeref here!
|
||||
case n.kind
|
||||
of nkSym:
|
||||
# we don't own the location so it escapes:
|
||||
if n.sym.owner != p.prc:
|
||||
return true
|
||||
elif inTryStmt and sfUsedInFinallyOrExcept in n.sym.flags:
|
||||
# it is also an observable store if the location is used
|
||||
# in 'except' or 'finally'
|
||||
return true
|
||||
return false
|
||||
of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
|
||||
nkCheckedFieldExpr:
|
||||
n = n[0]
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
n = n[1]
|
||||
else:
|
||||
# cannot analyse the location; assume the worst
|
||||
return true
|
||||
|
||||
proc leftAppearsOnRightSide(le, ri: PNode): bool =
|
||||
if le != nil:
|
||||
for i in 1..<ri.len:
|
||||
for i in 1 ..< ri.len:
|
||||
let r = ri[i]
|
||||
if isPartOf(le, r) != arNo: return true
|
||||
# we use the weaker 'canRaise' here in order to prevent too many
|
||||
# annoying warnings, see #14514
|
||||
if canRaise(ri[0]) and
|
||||
locationEscapes(p, le, p.nestedTryStmts.len > 0):
|
||||
message(p.config, le.info, warnObservableStores, $le)
|
||||
# bug #19613 prevent dangerous aliasing too:
|
||||
if dest != nil and dest != le:
|
||||
for i in 1..<ri.len:
|
||||
let r = ri[i]
|
||||
if isPartOf(dest, r) != arNo: return true
|
||||
|
||||
proc hasNoInit(call: PNode): bool {.inline.} =
|
||||
result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
|
||||
|
||||
proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
|
||||
if d.k in {locTemp, locNone} or not canRaise:
|
||||
result = true
|
||||
elif d.k == locLocalVar and p.withinTryWithExcept == 0:
|
||||
# we cannot observe a store to a local variable if the current proc
|
||||
# has no error handler:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
|
||||
|
||||
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
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[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ.sons[0]):
|
||||
if params != nil: 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):
|
||||
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
|
||||
# Great, we can use 'd':
|
||||
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add(~");$n")
|
||||
add(pl, addrLoc(p.config, d))
|
||||
add(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ[0], tmp, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add(~");$n")
|
||||
getTemp(p, typ.sons[0], tmp, needsInit=true)
|
||||
add(pl, addrLoc(p.config, tmp))
|
||||
add(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
pl.add(~")")
|
||||
add(pl, ~")")
|
||||
if p.module.compileToCpp:
|
||||
if lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
@@ -113,98 +56,29 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone and p.splitDecls == 0:
|
||||
getTempCpp(p, typ[0], d, pl)
|
||||
getTempCpp(p, typ.sons[0], d, pl)
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, typ[0], d)
|
||||
if d.k == locNone: getTemp(p, typ.sons[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):
|
||||
if d.k == locNone: getTemp(p, typ[0], d)
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to 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)
|
||||
else:
|
||||
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:
|
||||
pl.add(~");$n")
|
||||
add(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
|
||||
|
||||
proc reifiedOpenArray(n: PNode): bool {.inline.} =
|
||||
var x = n
|
||||
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 openArrayLoc(p: BProc, n: PNode): Rope =
|
||||
var a: TLoc
|
||||
|
||||
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (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)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
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 = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("($4*)($1)+(($2)-($3))" %
|
||||
[rdLoc(a), rdLoc(b), intLiteral(first), dest],
|
||||
lengthExpr)
|
||||
of tyOpenArray, tyVarargs:
|
||||
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 = ("($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:
|
||||
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:
|
||||
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
|
||||
lengthExpr)
|
||||
else:
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
|
||||
var q = skipConv(n)
|
||||
var skipped = false
|
||||
while q.kind == nkStmtListExpr and q.len > 0:
|
||||
@@ -215,37 +89,49 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
|
||||
if skipped:
|
||||
q = skipConv(n)
|
||||
while q.kind == nkStmtListExpr and q.len > 0:
|
||||
for i in 0..<q.len-1:
|
||||
for i in 0..q.len-2:
|
||||
genStmts(p, q[i])
|
||||
q = q.lastSon
|
||||
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
|
||||
result = x & ", " & y
|
||||
else:
|
||||
var a: TLoc
|
||||
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(n):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
result = "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
else:
|
||||
result = "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
var 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)
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||
let dest = getTypeDesc(p.module, n.typ.sons[0])
|
||||
case ty.kind
|
||||
of tyArray:
|
||||
let first = toInt64(firstOrd(p.config, ty))
|
||||
if first == 0:
|
||||
result = "($4*)(($1)+($2)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
|
||||
else:
|
||||
result = "$1, $1Len_0" % [rdLoc(a)]
|
||||
result = "($5*)($1)+(($2)-($4)), ($3)-($2)+1" %
|
||||
[rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first), dest]
|
||||
of tyOpenArray, tyVarargs, tyUncheckedArray, tyCString:
|
||||
result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
|
||||
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:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar and
|
||||
not compileToCpp(p.module):
|
||||
result = "($5*)(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest]
|
||||
else:
|
||||
result = "($5*)$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest]
|
||||
else:
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
case skipTypes(a.t, abstractVar).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = "$1, $1Len_0" % [rdLoc(a)]
|
||||
of tyString, tySequence:
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar and
|
||||
not compileToCpp(p.module):
|
||||
var t: TLoc
|
||||
t.r = "(*$1)" % [a.rdLoc]
|
||||
result = "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
|
||||
else:
|
||||
result = "($4) ? ($1$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
|
||||
result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
|
||||
of tyArray:
|
||||
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||
of tyPtr, tyRef:
|
||||
@@ -253,54 +139,34 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
|
||||
of tyString, tySequence:
|
||||
var t: TLoc
|
||||
t.r = "(*$1)" % [a.rdLoc]
|
||||
result = "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
|
||||
of tyArray:
|
||||
result = "$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))
|
||||
|
||||
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, gcOrc} and
|
||||
getSize(p.config, a.lode.typ) < 1024:
|
||||
getTemp(p, a.lode.typ, result, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
else:
|
||||
result = a
|
||||
|
||||
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, needsTmp: bool): Rope {.inline.} =
|
||||
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n[0], a)
|
||||
ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
result = ropecg(p.module, "#nimToCStringConv($1)", [a.rdLoc])
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope =
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
result = genArgStringToCString(p, n, needsTmp)
|
||||
result = genArgStringToCString(p, n)
|
||||
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||
var n = if n.kind != nkHiddenAddr: n else: n[0]
|
||||
result = openArrayLoc(p, param.typ, n)
|
||||
var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
|
||||
result = openArrayLoc(p, n)
|
||||
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
|
||||
initLocExpr(p, n, a)
|
||||
if n.kind in {nkCharLit..nkNilLit}:
|
||||
result = addrLoc(p.config, literalsNeedsTmp(p, a))
|
||||
else:
|
||||
result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp))
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
result = addrLoc(p.config, a)
|
||||
elif p.module.compileToCpp and param.typ.kind == tyVar and
|
||||
n.kind == nkHiddenAddr:
|
||||
initLocExprSingleUse(p, n[0], a)
|
||||
initLocExprSingleUse(p, n.sons[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]
|
||||
let callee = call.sons[0]
|
||||
if callee.kind == nkSym and
|
||||
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||
@@ -309,101 +175,26 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rop
|
||||
result = rdLoc(a)
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||
#assert result != nil
|
||||
result = rdLoc(a)
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope =
|
||||
proc genArgNoParam(p: BProc, n: PNode): Rope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
result = genArgStringToCString(p, n, needsTmp)
|
||||
result = genArgStringToCString(p, n)
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||
result = rdLoc(a)
|
||||
|
||||
from dfa import aliases, AliasKind
|
||||
|
||||
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
|
||||
for p in potentialWrites:
|
||||
if p.aliases(n) != no or n.aliases(p) != no:
|
||||
return true
|
||||
|
||||
proc skipTrivialIndirections(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv:
|
||||
result = result[0]
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
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:
|
||||
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)
|
||||
template genParamLoop(params) {.dirty.} =
|
||||
if i < len(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
let paramType = typ.n.sons[i]
|
||||
if not paramType.typ.isCompileTimeOnly:
|
||||
if params != nil: add(params, ~", ")
|
||||
add(params, genArg(p, ri.sons[i], paramType.sym, ri))
|
||||
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
|
||||
of nkSym: result.add n
|
||||
else:
|
||||
for s in n:
|
||||
getPotentialReads(s, result)
|
||||
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType): 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]
|
||||
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]
|
||||
getPotentialReads(ri[i], potentialReads)
|
||||
for n in potentialReads:
|
||||
if not needTmp[i - 1]:
|
||||
needTmp[i - 1] = potentialAlias(n, potentialWrites)
|
||||
getPotentialWrites(ri[i], false, potentialWrites)
|
||||
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
|
||||
|
||||
for i in 1..<ri.len:
|
||||
if i < typ.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
let paramType = typ.n[i]
|
||||
if not paramType.typ.isCompileTimeOnly:
|
||||
if result != nil: result.add(~", ")
|
||||
result.add(genArg(p, ri[i], paramType.sym, ri, needTmp[i-1]))
|
||||
else:
|
||||
if result != nil: result.add(~", ")
|
||||
result.add(genArgNoParam(p, ri[i], needTmp[i-1]))
|
||||
if params != nil: add(params, ~", ")
|
||||
add(params, genArgNoParam(p, ri.sons[i]))
|
||||
|
||||
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
||||
@@ -413,36 +204,40 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var op: TLoc
|
||||
# this is a hotspot in the compiler
|
||||
initLocExpr(p, ri[0], op)
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var params: Rope
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
|
||||
var params = genParams(p, ri, typ)
|
||||
|
||||
assert(len(typ) == len(typ.n))
|
||||
var length = len(ri)
|
||||
for i in 1 ..< length:
|
||||
genParamLoop(params)
|
||||
var callee = rdLoc(op)
|
||||
if p.hcrOn and ri[0].kind == nkSym:
|
||||
callee.addActualSuffixForHCR(p.module.module, ri[0].sym)
|
||||
if p.hcrOn and ri.sons[0].kind == nkSym:
|
||||
callee.addActualSuffixForHCR(p.module.module, ri.sons[0].sym)
|
||||
fixupCall(p, le, ri, d, callee, params)
|
||||
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
proc getRawProcType(p: BProc, t: PType): Rope =
|
||||
result = getClosureType(p.module, t, clHalf)
|
||||
|
||||
proc addComma(r: Rope): Rope =
|
||||
if r == nil: r else: r & ~", "
|
||||
result = if r == nil: r else: r & ~", "
|
||||
|
||||
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: TLoc
|
||||
initLocExpr(p, ri[0], op)
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl: Rope
|
||||
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
|
||||
var pl = genParams(p, ri, typ)
|
||||
var length = len(ri)
|
||||
for i in 1 ..< length:
|
||||
assert(len(typ) == len(typ.n))
|
||||
genParamLoop(pl)
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
if tfIterator in typ.flags:
|
||||
@@ -450,42 +245,28 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
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[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if ri.len > 1: pl.add(~", ")
|
||||
let rawProc = getRawProcType(p, typ)
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ.sons[0]):
|
||||
if len(ri) > 1: add(pl, ~", ")
|
||||
# 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):
|
||||
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
|
||||
# Great, we can use 'd':
|
||||
if d.k == locNone:
|
||||
getTemp(p, typ[0], d, needsInit=true)
|
||||
getTemp(p, typ.sons[0], d, needsInit=true)
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
add(pl, addrLoc(p.config, d))
|
||||
genCallPattern()
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ[0], tmp, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
getTemp(p, typ.sons[0], tmp, needsInit=true)
|
||||
add(pl, 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: getTemp(p, typ[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
if tfIterator in typ.flags:
|
||||
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
else:
|
||||
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[0], tmp)
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
@@ -493,31 +274,29 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
else:
|
||||
list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
genAssignment(p, tmp, list, {})
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
if i < typ.len:
|
||||
if i < len(typ):
|
||||
# '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]
|
||||
let paramType = typ.n.sons[i]
|
||||
assert(paramType.kind == nkSym)
|
||||
if paramType.typ.isCompileTimeOnly:
|
||||
result = nil
|
||||
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
||||
result = genArgNoParam(p, ri[i][0])
|
||||
elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
|
||||
result = genArgNoParam(p, ri.sons[i][0])
|
||||
else:
|
||||
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
|
||||
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
|
||||
else:
|
||||
if tfVarargs notin typ.flags:
|
||||
localError(p.config, ri.info, "wrong argument count")
|
||||
result = nil
|
||||
else:
|
||||
result = genArgNoParam(p, ri[i])
|
||||
result = genArgNoParam(p, ri.sons[i])
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
@@ -561,16 +340,16 @@ proc skipAddrDeref(node: PNode): PNode =
|
||||
var isAddr = false
|
||||
case n.kind
|
||||
of nkAddr, nkHiddenAddr:
|
||||
n = n[0]
|
||||
n = n.sons[0]
|
||||
isAddr = true
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
n = n[0]
|
||||
n = n.sons[0]
|
||||
else: return n
|
||||
if n.kind == nkObjDownConv: n = n[0]
|
||||
if n.kind == nkObjDownConv: n = n.sons[0]
|
||||
if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}:
|
||||
result = n[0]
|
||||
result = n.sons[0]
|
||||
elif n.kind in {nkAddr, nkHiddenAddr}:
|
||||
result = n[0]
|
||||
result = n.sons[0]
|
||||
else:
|
||||
result = node
|
||||
|
||||
@@ -578,14 +357,14 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): 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.len
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
internalAssert p.config, i < len(typ)
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
# if the parameter is lying (tyVar) and thus we required an additional deref,
|
||||
# skip the deref:
|
||||
var ri = ri[i]
|
||||
while ri.kind == nkObjDownConv: ri = ri[0]
|
||||
let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if t.kind in {tyVar}:
|
||||
let t = typ.sons[i].skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if t.kind == tyVar:
|
||||
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
|
||||
if x.typ.kind == tyPtr:
|
||||
result = genArgNoParam(p, x)
|
||||
@@ -606,7 +385,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
else:
|
||||
ri = skipAddrDeref(ri)
|
||||
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
|
||||
result = genArgNoParam(p, ri) #, typ.n[i].sym)
|
||||
result = genArgNoParam(p, ri) #, typ.n.sons[i].sym)
|
||||
result.add(".")
|
||||
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
@@ -616,7 +395,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
case pat[i]
|
||||
of '@':
|
||||
var first = true
|
||||
for k in j..<ri.len:
|
||||
for k in j ..< ri.len:
|
||||
let arg = genOtherArg(p, ri, k, typ)
|
||||
if arg.len > 0:
|
||||
if not first:
|
||||
@@ -628,12 +407,12 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
||||
let ri = ri[j]
|
||||
if ri.kind in nkCallKinds:
|
||||
let typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if pat[i+1] == '+': result.add genArgNoParam(p, ri[0])
|
||||
let typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0])
|
||||
result.add(~"(")
|
||||
if 1 < ri.len:
|
||||
result.add genOtherArg(p, ri, 1, typ)
|
||||
for k in j+1..<ri.len:
|
||||
for k in j+1 ..< ri.len:
|
||||
result.add(~", ")
|
||||
result.add genOtherArg(p, ri, k, typ)
|
||||
result.add(~")")
|
||||
@@ -644,7 +423,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
result.add genThisArg(p, ri, j, typ)
|
||||
inc i
|
||||
elif i+1 < pat.len and pat[i+1] == '[':
|
||||
var arg = ri[j].skipAddrDeref
|
||||
var arg = ri.sons[j].skipAddrDeref
|
||||
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
|
||||
result.add genArgNoParam(p, arg)
|
||||
#result.add debugTree(arg, 0, 10)
|
||||
@@ -664,23 +443,24 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
if pat[i] notin {'@', '#', '\''}: inc(i)
|
||||
else: break
|
||||
if i - 1 >= start:
|
||||
result.add(substr(pat, start, i - 1))
|
||||
add(result, substr(pat, start, i - 1))
|
||||
|
||||
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var op: TLoc
|
||||
initLocExpr(p, ri[0], op)
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
var length = len(ri)
|
||||
assert(len(typ) == len(typ.n))
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = ri[0].sym.loc.r.data
|
||||
let pat = ri.sons[0].sym.loc.r.data
|
||||
internalAssert p.config, pat.len > 0
|
||||
if pat.contains({'#', '(', '@', '\''}):
|
||||
var pl = genPatternCall(p, ri, pat, typ)
|
||||
# simpler version of 'fixupCall' that works with the pl+params combination:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ[0] != nil:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if typ.sons[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
|
||||
@@ -689,131 +469,116 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
d.r = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, typ[0], d)
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add(~";$n")
|
||||
add(pl, ~";$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var pl: Rope = nil
|
||||
#var param = typ.n[1].sym
|
||||
#var param = typ.n.sons[1].sym
|
||||
if 1 < ri.len:
|
||||
pl.add(genThisArg(p, ri, 1, typ))
|
||||
pl.add(op.r)
|
||||
add(pl, genThisArg(p, ri, 1, typ))
|
||||
add(pl, op.r)
|
||||
var params: Rope
|
||||
for i in 2..<ri.len:
|
||||
for i in 2 ..< length:
|
||||
if params != nil: params.add(~", ")
|
||||
assert(typ.len == typ.n.len)
|
||||
params.add(genOtherArg(p, ri, i, typ))
|
||||
assert(len(typ) == len(typ.n))
|
||||
add(params, genOtherArg(p, ri, i, typ))
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
var op: TLoc
|
||||
initLocExpr(p, ri[0], op)
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl = ~"["
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
var length = len(ri)
|
||||
assert(len(typ) == len(typ.n))
|
||||
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = ri[0].sym.loc.r.data
|
||||
let pat = ri.sons[0].sym.loc.r.data
|
||||
internalAssert p.config, pat.len > 0
|
||||
var start = 3
|
||||
if ' ' in pat:
|
||||
start = 1
|
||||
pl.add(op.r)
|
||||
if ri.len > 1:
|
||||
pl.add(~": ")
|
||||
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
|
||||
add(pl, op.r)
|
||||
if length > 1:
|
||||
add(pl, ~": ")
|
||||
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
|
||||
start = 2
|
||||
else:
|
||||
if ri.len > 1:
|
||||
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
|
||||
pl.add(~" ")
|
||||
pl.add(op.r)
|
||||
if ri.len > 2:
|
||||
pl.add(~": ")
|
||||
pl.add(genArg(p, ri[2], typ.n[2].sym, ri))
|
||||
for i in start..<ri.len:
|
||||
assert(typ.len == typ.n.len)
|
||||
if i >= typ.len:
|
||||
if length > 1:
|
||||
add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri))
|
||||
add(pl, ~" ")
|
||||
add(pl, op.r)
|
||||
if length > 2:
|
||||
add(pl, ~": ")
|
||||
add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri))
|
||||
for i in start ..< length:
|
||||
assert(len(typ) == len(typ.n))
|
||||
if i >= len(typ):
|
||||
internalError(p.config, ri.info, "varargs for objective C method?")
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
var param = typ.n[i].sym
|
||||
pl.add(~" ")
|
||||
pl.add(param.name.s)
|
||||
pl.add(~": ")
|
||||
pl.add(genArg(p, ri[i], param, ri))
|
||||
if typ[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if ri.len > 1: pl.add(~" ")
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
var param = typ.n.sons[i].sym
|
||||
add(pl, ~" ")
|
||||
add(pl, param.name.s)
|
||||
add(pl, ~": ")
|
||||
add(pl, genArg(p, ri.sons[i], param, ri))
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ.sons[0]):
|
||||
if len(ri) > 1: add(pl, ~" ")
|
||||
# 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: getTemp(p, typ[0], d, needsInit=true)
|
||||
pl.add(~"Result: ")
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add(~"];$n")
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
|
||||
add(pl, ~"Result: ")
|
||||
add(pl, addrLoc(p.config, d))
|
||||
add(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ[0], tmp, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add(~"];$n")
|
||||
getTemp(p, typ.sons[0], tmp, needsInit=true)
|
||||
add(pl, addrLoc(p.config, tmp))
|
||||
add(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add(~"]")
|
||||
if d.k == locNone: getTemp(p, typ[0], d)
|
||||
add(pl, ~"]")
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, ri, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add(~"];$n")
|
||||
add(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||
let r = getRoot(n)
|
||||
result = r != nil and r.loc.lode == nil
|
||||
|
||||
proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
|
||||
#[ Consider this example.
|
||||
|
||||
var :tmpD_3281815
|
||||
try:
|
||||
if true:
|
||||
return
|
||||
let args_3280013 =
|
||||
wasMoved_3281816(:tmpD_3281815)
|
||||
`=_3280036`(:tmpD_3281815, [1])
|
||||
:tmpD_3281815
|
||||
finally:
|
||||
`=destroy_3280027`(args_3280013)
|
||||
|
||||
We want to return early but the 'finally' section is traversed before
|
||||
the 'let args = ...' statement. We exploit this to generate better
|
||||
code for 'return'. ]#
|
||||
result = e.len == 2 and e[0].kind == nkSym and
|
||||
e[0].sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr)
|
||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||
if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
||||
genClosureCall(p, nil, e, d)
|
||||
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
|
||||
genInfixCall(p, nil, e, d)
|
||||
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
|
||||
genNamedParamCall(p, e, d)
|
||||
else:
|
||||
genPrefixCall(p, nil, e, d)
|
||||
postStmtActions(p)
|
||||
|
||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri):
|
||||
return
|
||||
if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
||||
if ri.sons[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:
|
||||
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
|
||||
genInfixCall(p, le, ri, d)
|
||||
elif ri[0].kind == nkSym and sfNamedParamCall in ri[0].sym.flags:
|
||||
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
|
||||
genNamedParamCall(p, ri, d)
|
||||
else:
|
||||
genPrefixCall(p, le, ri, d)
|
||||
|
||||
proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d)
|
||||
postStmtActions(p)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,8 +35,8 @@ proc detectSeqVersion(m: BModule): int =
|
||||
proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
|
||||
discard cgsym(m, "TGenericSeq")
|
||||
result = getTempName(m)
|
||||
m.s[cfsData].addf("STRING_LITERAL($1, $2, $3);$n",
|
||||
[result, makeCString(s), rope(s.len)])
|
||||
addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
|
||||
[result, makeCString(s), rope(len(s))])
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode): Rope =
|
||||
if s.isNil:
|
||||
@@ -53,31 +53,29 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope =
|
||||
|
||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
||||
m.s[cfsData].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 genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope) =
|
||||
addf(m.s[cfsData], "static const struct {$n" &
|
||||
" NI cap; void* allocator; NIM_CHAR data[$2+1];$n" &
|
||||
"} $1 = { $2, NIM_NIL, $3 };$n",
|
||||
[result, rope(len(s)), makeCString(s)])
|
||||
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): Rope =
|
||||
proc genStringLiteralV2(m: BModule; n: PNode): Rope =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
if id == m.labels:
|
||||
let pureLit = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit)
|
||||
result = getTempName(m)
|
||||
discard cgsym(m, "NimStrPayload")
|
||||
discard cgsym(m, "NimStringV2")
|
||||
# string literal not found in the cache:
|
||||
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
||||
addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, rope(len(n.strVal)), pureLit])
|
||||
else:
|
||||
result = getTempName(m)
|
||||
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, rope(n.strVal.len), m.tmpBase & rope(id),
|
||||
rope(if isConst: "const" else: "")])
|
||||
addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, rope(len(n.strVal)), m.tmpBase & rope(id)])
|
||||
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope =
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode): Rope =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var pureLit: Rope
|
||||
if id == m.labels:
|
||||
@@ -85,20 +83,19 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope =
|
||||
discard cgsym(m, "NimStrPayload")
|
||||
discard cgsym(m, "NimStringV2")
|
||||
# string literal not found in the cache:
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit)
|
||||
else:
|
||||
pureLit = m.tmpBase & rope(id)
|
||||
result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit]
|
||||
result = "{$1, (NimStrPayload*)&$2}" % [rope(len(n.strVal)), pureLit]
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
isConst: bool): Rope =
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
|
||||
of 2:
|
||||
result = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, result, isConst)
|
||||
genStringLiteralDataOnlyV2(m, s, result)
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
@@ -108,6 +105,6 @@ proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
|
||||
proc genStringLiteral(m: BModule; n: PNode): Rope =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: result = genStringLiteralV1(m, n)
|
||||
of 2: result = genStringLiteralV2(m, n, isConst = true)
|
||||
of 2: result = genStringLiteralV2(m, n)
|
||||
else:
|
||||
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
||||
|
||||
@@ -28,8 +28,8 @@ const
|
||||
cfsFieldInfo: "NIM_merge_FIELD_INFO",
|
||||
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
||||
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
||||
cfsData: "NIM_merge_DATA",
|
||||
cfsVars: "NIM_merge_VARS",
|
||||
cfsData: "NIM_merge_DATA",
|
||||
cfsProcs: "NIM_merge_PROCS",
|
||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||
@@ -48,15 +48,11 @@ const
|
||||
NimMergeEndMark = "/*\tNIM_merge_END:*/"
|
||||
|
||||
proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
|
||||
# useful for debugging and only adds at most a few lines in each file
|
||||
result.add("\n/* section: ")
|
||||
result.add(CFileSectionNames[fs])
|
||||
result.add(" */\n")
|
||||
if compilationCachePresent(conf):
|
||||
result = nil
|
||||
result.add("\n/*\t")
|
||||
result.add(CFileSectionNames[fs])
|
||||
result.add(":*/\n")
|
||||
add(result, "\n/*\t")
|
||||
add(result, CFileSectionNames[fs])
|
||||
add(result, ":*/\n")
|
||||
|
||||
proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
@@ -65,9 +61,9 @@ proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
|
||||
proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope("")
|
||||
result.add("\n/*\t")
|
||||
result.add(CProcSectionNames[ps])
|
||||
result.add(":*/\n")
|
||||
add(result, "\n/*\t")
|
||||
add(result, CProcSectionNames[ps])
|
||||
add(result, ":*/\n")
|
||||
|
||||
proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
@@ -180,6 +176,10 @@ proc readKey(L: var TBaseLexer, result: var string) =
|
||||
if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
|
||||
L.bufpos = pos + 1 # skip ':'
|
||||
|
||||
proc newFakeType(id: int): PType =
|
||||
new(result)
|
||||
result.id = id
|
||||
|
||||
proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
|
||||
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
|
||||
inc L.bufpos
|
||||
@@ -188,7 +188,10 @@ proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
|
||||
var key = decodeStr(L.buf, L.bufpos)
|
||||
if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
|
||||
inc L.bufpos
|
||||
discard decodeStr(L.buf, L.bufpos)
|
||||
var value = decodeStr(L.buf, L.bufpos)
|
||||
# XXX implement me
|
||||
when false:
|
||||
idTablePut(result, newFakeType(key), value.rope)
|
||||
inc L.bufpos
|
||||
|
||||
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
|
||||
@@ -218,6 +221,9 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
|
||||
else: doAssert(false, "ccgmerge: unknown key: " & k)
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template withCFile(cfilename: AbsoluteFile, body: untyped) =
|
||||
var s = llStreamOpen(cfilename, fmRead)
|
||||
if s == nil: return
|
||||
@@ -272,7 +278,7 @@ proc mergeRequired*(m: BModule): bool =
|
||||
if m.s[i] != nil:
|
||||
#echo "not empty: ", i, " ", m.s[i]
|
||||
return true
|
||||
for i in TCProcSection:
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
if m.initProc.s(i) != nil:
|
||||
#echo "not empty: ", i, " ", m.initProc.s[i]
|
||||
return true
|
||||
@@ -282,7 +288,7 @@ proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
|
||||
var old: TMergeSections
|
||||
readMergeSections(cfilename, old)
|
||||
# do the merge; old section before new section:
|
||||
for i in TCFileSection:
|
||||
for i in low(TCFileSection)..high(TCFileSection):
|
||||
m.s[i] = old.f[i] & m.s[i]
|
||||
for i in TCProcSection:
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
m.initProc.s(i) = old.p[i] & m.initProc.s(i)
|
||||
@@ -1,107 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
## Code specialization instead of the old, incredibly slow 'genericReset'
|
||||
## implementation.
|
||||
|
||||
proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
|
||||
|
||||
proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||
typ: PType) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
specializeResetN(p, accessor, n[i], typ)
|
||||
of nkRecCase:
|
||||
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
||||
let disc = n[0].sym
|
||||
if disc.loc.r == nil: fillObjectFields(p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
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.r == nil: fillObjectFields(p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
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) =
|
||||
if typ == nil: return
|
||||
|
||||
case typ.kind
|
||||
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
||||
tySink, tyOwned:
|
||||
specializeResetT(p, accessor, lastSon(typ))
|
||||
of tyArray:
|
||||
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:
|
||||
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 in 0..<typ.len:
|
||||
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
|
||||
|
||||
of tyString, tyRef, tySequence:
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
|
||||
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
else:
|
||||
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:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
of tySet:
|
||||
case mapSetType(p.config, typ)
|
||||
of ctArray:
|
||||
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
||||
[accessor, getTypeDesc(p.module, typ)])
|
||||
of ctInt8, ctInt16, ctInt32, ctInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
else:
|
||||
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}:
|
||||
discard
|
||||
|
||||
proc specializeReset(p: BProc, a: TLoc) =
|
||||
specializeResetT(p, rdLoc(a), a.t)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Thread var support for architectures that lack native support for
|
||||
## thread local storage.
|
||||
## Thread var support for crappy architectures that lack native support for
|
||||
## thread local storage. (**Thank you Mac OS X!**)
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
@@ -16,11 +16,11 @@ proc emulatedThreadVars(conf: ConfigRef): bool =
|
||||
result = {optThreads, optTlsEmulation} <= conf.globalOptions
|
||||
|
||||
proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
|
||||
p.flags.incl threadVarAccessed
|
||||
if emulatedThreadVars(p.config) and not p.threadVarAccessed:
|
||||
p.threadVarAccessed = true
|
||||
incl p.module.flags, usesThreadVars
|
||||
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
||||
p.procSec(cpsInit).add(
|
||||
addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", [])
|
||||
add(p.procSec(cpsInit),
|
||||
ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
|
||||
|
||||
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
@@ -30,30 +30,23 @@ 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.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
addf(m.g.nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
else:
|
||||
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])
|
||||
if isExtern: add(m.s[cfsVars], "extern ")
|
||||
if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
|
||||
add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
addf(m.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if m.g.nimtv != nil 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].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv != nil:
|
||||
let externc = if m.config.backend == backendCpp or
|
||||
let externc = if m.config.cmd == cmdCompileToCpp or
|
||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
||||
else: ""
|
||||
m.s[cfsProcs].addf(
|
||||
addf(m.s[cfsProcs],
|
||||
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
||||
[externc.rope])
|
||||
|
||||
@@ -28,18 +28,18 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
genTraverseProc(c, accessor, n[i], typ)
|
||||
for i in 0 ..< len(n):
|
||||
genTraverseProc(c, accessor, n.sons[i], typ)
|
||||
of nkRecCase:
|
||||
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
||||
if (n.sons[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
||||
var p = c.p
|
||||
let disc = n[0].sym
|
||||
let disc = n.sons[0].sym
|
||||
if disc.loc.r == nil: fillObjectFields(c.p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
for i in 1 ..< len(n):
|
||||
let branch = n.sons[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
@@ -73,33 +73,33 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
tySink, tyOwned:
|
||||
genTraverseProc(c, accessor, lastSon(typ))
|
||||
of tyArray:
|
||||
let arraySize = lengthOrd(c.p.config, typ[0])
|
||||
let arraySize = lengthOrd(c.p.config, typ.sons[0])
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
|
||||
let oldCode = p.s(cpsStmts)
|
||||
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])
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ.sons[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:
|
||||
for i in 0..<typ.len:
|
||||
var x = typ[i]
|
||||
for i in 0 ..< len(typ):
|
||||
var x = typ.sons[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 in 0..<typ.len:
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
|
||||
for i in 0 ..< len(typ):
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ.sons[i])
|
||||
of tyRef:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
of tySequence:
|
||||
if optSeqDestructors notin c.p.module.config.globalOptions:
|
||||
if tfHasAsgn notin typ.flags:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
elif containsGarbageCollectedRef(typ.lastSon):
|
||||
# destructor based seqs are themselves not traced but their data is, if
|
||||
@@ -119,7 +119,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
var p = c.p
|
||||
assert typ.kind == tySequence
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
|
||||
let oldCode = p.s(cpsStmts)
|
||||
var a: TLoc
|
||||
a.r = accessor
|
||||
@@ -127,7 +127,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
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])
|
||||
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.sons[0])
|
||||
if p.s(cpsStmts).len == oldLen:
|
||||
# do not emit dummy long loops for faster debug builds:
|
||||
p.s(cpsStmts) = oldCode
|
||||
@@ -155,11 +155,11 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
if typ.kind == tySequence:
|
||||
genTraverseProcSeq(c, "a".rope, typ)
|
||||
else:
|
||||
if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
|
||||
if skipTypes(typ.sons[0], typedescInst+{tyOwned}).kind == tyArray:
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".rope, typ[0])
|
||||
genTraverseProc(c, "a".rope, typ.sons[0])
|
||||
else:
|
||||
genTraverseProc(c, "(*a)".rope, typ[0])
|
||||
genTraverseProc(c, "(*a)".rope, typ.sons[0])
|
||||
|
||||
let generatedProc = "$1 {$n$2$3$4}\n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
@@ -168,12 +168,12 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
|
||||
if hcrOn:
|
||||
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",
|
||||
addf(m.s[cfsProcHeaders], "N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
|
||||
addf(m.s[cfsDynLibInit], "\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)
|
||||
discard genTypeInfo(m, s.loc.t, info)
|
||||
|
||||
var c: TTraversalClosure
|
||||
var p = newProc(nil, m)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,17 +10,17 @@
|
||||
# This module declares some helpers for the C code generator.
|
||||
|
||||
import
|
||||
ast, types, hashes, strutils, msgs, wordrecg,
|
||||
platform, trees, options, cgendata
|
||||
ast, hashes, strutils, msgs, wordrecg,
|
||||
platform, trees, options
|
||||
|
||||
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||
case n.kind
|
||||
of nkStmtList:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
result = getPragmaStmt(n[i], w)
|
||||
if result != nil: break
|
||||
of nkPragma:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
if whichPragma(n[i]) == w: return n[i]
|
||||
else: discard
|
||||
|
||||
@@ -33,7 +33,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt =
|
||||
# we have to use the same bitwidth
|
||||
# as the target CPU
|
||||
var b = 0'u64
|
||||
for i in 0..<s.len:
|
||||
for i in 0 ..< len(s):
|
||||
b = b + uint(s[i])
|
||||
b = b + (b shl 10)
|
||||
b = b xor (b shr 6)
|
||||
@@ -43,7 +43,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt =
|
||||
result = cast[Hash](b)
|
||||
else:
|
||||
var a = 0'u32
|
||||
for i in 0..<s.len:
|
||||
for i in 0 ..< len(s):
|
||||
a = a + uint32(s[i])
|
||||
a = a + (a shl 10)
|
||||
a = a xor (a shr 6)
|
||||
@@ -70,18 +70,18 @@ proc mangle*(name: string): string =
|
||||
template special(x) =
|
||||
result.add x
|
||||
requiresUnderscore = true
|
||||
for i in start..<name.len:
|
||||
for i in start..(name.len-1):
|
||||
let c = name[i]
|
||||
case c
|
||||
of 'a'..'z', '0'..'9', 'A'..'Z':
|
||||
result.add(c)
|
||||
add(result, 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)
|
||||
add(result, c)
|
||||
of '$': special "dollar"
|
||||
of '%': special "percent"
|
||||
of '&': special "amp"
|
||||
@@ -102,43 +102,7 @@ proc mangle*(name: string): string =
|
||||
of '@': special "at"
|
||||
of '|': special "bar"
|
||||
else:
|
||||
result.add("X" & toHex(ord(c), 2))
|
||||
add(result, "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
|
||||
of 2: result = ctInt16
|
||||
of 4: result = ctInt32
|
||||
of 8: result = ctInt64
|
||||
else: result = ctArray
|
||||
|
||||
proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
var pt = skipTypes(s.typ, typedescInst)
|
||||
assert skResult != s.kind
|
||||
|
||||
if tfByRef in pt.flags: return true
|
||||
elif tfByCopy in pt.flags: return false
|
||||
case pt.kind
|
||||
of tyObject:
|
||||
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
|
||||
# forwarded objects are *always* passed by pointers for consistency!
|
||||
result = true
|
||||
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
|
||||
result = true # requested anyway
|
||||
elif (tfFinal in pt.flags) and (pt[0] == nil):
|
||||
result = false # no need, because no subtyping possible
|
||||
else:
|
||||
result = true # ordinary objects are always passed by reference,
|
||||
# otherwise casting doesn't work
|
||||
of tyTuple:
|
||||
result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
|
||||
else:
|
||||
result = false
|
||||
# first parameter and return type is 'lent T'? --> use pass by pointer
|
||||
if s.position == 0 and retType != nil and retType.kind == tyLent:
|
||||
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
|
||||
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
|
||||
|
||||
|
||||
1053
compiler/cgen.nim
1053
compiler/cgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options, intsets,
|
||||
tables, ndi, lineinfos, pathutils, modulegraphs, sets
|
||||
tables, ndi, lineinfos, pathutils, modulegraphs
|
||||
|
||||
type
|
||||
TLabel* = Rope # for the C generator a label is just a rope
|
||||
@@ -25,10 +25,10 @@ type
|
||||
# this is needed for strange type generation
|
||||
# reasons
|
||||
cfsFieldInfo, # section for field information
|
||||
cfsTypeInfo, # section for type information (ag ABI checks)
|
||||
cfsTypeInfo, # section for type information
|
||||
cfsProcHeaders, # section for C procs prototypes
|
||||
cfsData, # section for C constant data
|
||||
cfsVars, # section for C variable declarations
|
||||
cfsData, # section for C constant data
|
||||
cfsProcs, # section for C procs that are not inline
|
||||
cfsInitProc, # section for the C init proc
|
||||
cfsDatInitProc, # section for the C datInit proc
|
||||
@@ -64,21 +64,16 @@ type
|
||||
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||
frameLen*: int16
|
||||
|
||||
TCProcFlag* = enum
|
||||
beforeRetNeeded,
|
||||
threadVarAccessed,
|
||||
hasCurFramePointer,
|
||||
noSafePoints,
|
||||
nimErrorFlagAccessed,
|
||||
nimErrorFlagDeclared,
|
||||
nimErrorFlagDisabled
|
||||
|
||||
TCProc = object # represents C proc that is currently generated
|
||||
prc*: PSym # the Nim proc that this C proc belongs to
|
||||
flags*: set[TCProcFlag]
|
||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||
hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
|
||||
# exception is generated
|
||||
noSafePoints*: bool # the proc doesn't use safe points in exception handling
|
||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
|
||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool]]
|
||||
# in how many nested try statements we are
|
||||
# (the vars must be volatile then)
|
||||
# bool is true when are in the except part of a try block
|
||||
@@ -91,18 +86,19 @@ type
|
||||
options*: TOptions # options that should be used for code
|
||||
# generation; this is the same as prc.options
|
||||
# unless prc == nil
|
||||
maxFrameLen*: int # max length of frame descriptor
|
||||
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
|
||||
# requires 'T x = T()' to become 'T x; x = T()'
|
||||
# (yes, C++ is weird like that)
|
||||
withinTryWithExcept*: int # required for goto based exception handling
|
||||
withinBlockLeaveActions*: int # complex to explain
|
||||
gcFrameId*: Natural # for the GC stack marking
|
||||
gcFrameType*: Rope # the struct {} we put the GC markers into
|
||||
sigConflicts*: CountTable[string]
|
||||
|
||||
TTypeSeq* = seq[PType]
|
||||
TypeCache* = Table[SigHash, Rope]
|
||||
TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]]
|
||||
TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: PSym]]
|
||||
|
||||
CodegenFlag* = enum
|
||||
preventStackTrace, # true if stack traces need to be prevented
|
||||
@@ -112,8 +108,7 @@ type
|
||||
isHeaderFile, # C source file is the header file
|
||||
includesStringh, # C source file already includes ``<string.h>``
|
||||
objHasKidsValid # whether we can rely on tfObjHasKids
|
||||
useAliveDataFromDce # use the `alive: IntSet` field instead of
|
||||
# computing alive data on our own.
|
||||
|
||||
|
||||
BModuleList* = ref object of RootObj
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
@@ -123,7 +118,6 @@ type
|
||||
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
||||
generatedHeader*: BModule
|
||||
typeInfoMarker*: TypeCacheWithOwner
|
||||
typeInfoMarkerV2*: TypeCacheWithOwner
|
||||
config*: ConfigRef
|
||||
graph*: ModuleGraph
|
||||
strVersion*, seqVersion*: int # version of the string/seq implementation to use
|
||||
@@ -148,17 +142,11 @@ type
|
||||
# without extension)
|
||||
tmpBase*: Rope # base for temp identifier generation
|
||||
typeCache*: TypeCache # cache the generated types
|
||||
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 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
|
||||
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*: Rope # type info globals are in here when HCR=on
|
||||
@@ -170,6 +158,7 @@ type
|
||||
labels*: Natural # for generating unique module-scope names
|
||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||
# OpenGL wrapper
|
||||
injectStmt*: Rope
|
||||
sigConflicts*: CountTable[SigHash]
|
||||
g*: BModuleList
|
||||
ndi*: NdiFile
|
||||
@@ -183,7 +172,7 @@ proc includeHeader*(this: BModule; header: string) =
|
||||
|
||||
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# section in the current block
|
||||
result = p.blocks[^1].sections[s]
|
||||
result = p.blocks[p.blocks.len-1].sections[s]
|
||||
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# top level proc sections
|
||||
@@ -193,15 +182,15 @@ proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
new(result)
|
||||
result.prc = prc
|
||||
result.module = module
|
||||
result.options = if prc != nil: prc.options
|
||||
else: module.config.options
|
||||
if prc != nil: result.options = prc.options
|
||||
else: result.options = module.config.options
|
||||
newSeq(result.blocks, 1)
|
||||
result.nestedTryStmts = @[]
|
||||
result.finallySafePoints = @[]
|
||||
result.sigConflicts = initCountTable[string]()
|
||||
|
||||
proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
|
||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: PSym]](),
|
||||
config: g.config, graph: g, nimtvDeclared: initIntSet())
|
||||
|
||||
iterator cgenModules*(g: BModuleList): BModule =
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements code generation for methods.
|
||||
## This module implements code generation for multi methods.
|
||||
|
||||
import
|
||||
intsets, options, ast, msgs, idents, renderer, types, magicsys,
|
||||
@@ -23,11 +23,11 @@ proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
||||
result = n
|
||||
elif diff < 0:
|
||||
result = newNodeIT(nkObjUpConv, n.info, d)
|
||||
result.add n
|
||||
addSon(result, n)
|
||||
if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
|
||||
elif diff > 0:
|
||||
result = newNodeIT(nkObjDownConv, n.info, d)
|
||||
result.add n
|
||||
addSon(result, n)
|
||||
if not downcast:
|
||||
internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
|
||||
else:
|
||||
@@ -44,36 +44,35 @@ proc getDispatcher*(s: PSym): PSym =
|
||||
proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||
result = n
|
||||
# replace ordinary method by dispatcher method:
|
||||
let disp = getDispatcher(result[0].sym)
|
||||
let disp = getDispatcher(result.sons[0].sym)
|
||||
if disp != nil:
|
||||
result[0].typ = disp.typ
|
||||
result[0].sym = disp
|
||||
result.sons[0].sym = disp
|
||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||
for i in 1..<result.len:
|
||||
result[i] = genConv(result[i], disp.typ[i], true, conf)
|
||||
for i in 1 ..< len(result):
|
||||
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true, conf)
|
||||
else:
|
||||
localError(conf, n.info, "'" & $result[0] & "' lacks a dispatcher")
|
||||
localError(conf, n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
|
||||
|
||||
type
|
||||
MethodResult = enum No, Invalid, Yes
|
||||
|
||||
proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
if a.name.id != b.name.id: return
|
||||
if a.typ.len != b.typ.len:
|
||||
if len(a.typ) != len(b.typ):
|
||||
return
|
||||
|
||||
for i in 1..<a.typ.len:
|
||||
var aa = a.typ[i]
|
||||
var bb = b.typ[i]
|
||||
for i in 1 ..< len(a.typ):
|
||||
var aa = a.typ.sons[i]
|
||||
var bb = b.typ.sons[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}:
|
||||
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}:
|
||||
aa = aa.lastSon
|
||||
bb = bb.lastSon
|
||||
else:
|
||||
break
|
||||
if sameType(a.typ[i], b.typ[i]):
|
||||
if sameType(a.typ.sons[i], b.typ.sons[i]):
|
||||
if aa.kind == tyObject and result != Invalid:
|
||||
result = Yes
|
||||
elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
|
||||
@@ -91,44 +90,41 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
return No
|
||||
if result == Yes:
|
||||
# check for return type:
|
||||
if not sameTypeOrNil(a.typ[0], b.typ[0]):
|
||||
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
|
||||
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]):
|
||||
if b.typ.sons[0] != nil and b.typ.sons[0].kind == tyUntyped:
|
||||
# infer 'auto' from the base to make it consistent:
|
||||
b.typ[0] = a.typ[0]
|
||||
b.typ.sons[0] = a.typ.sons[0]
|
||||
else:
|
||||
return No
|
||||
|
||||
proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||
if dispatcherPos < s.ast.len:
|
||||
# we've added a dispatcher already, so overwrite it
|
||||
s.ast[dispatcherPos] = dispatcher
|
||||
s.ast.sons[dispatcherPos] = dispatcher
|
||||
else:
|
||||
setLen(s.ast.sons, dispatcherPos+1)
|
||||
if s.ast[resultPos] == nil:
|
||||
s.ast[resultPos] = newNodeI(nkEmpty, s.info)
|
||||
s.ast[dispatcherPos] = dispatcher
|
||||
s.ast.sons[dispatcherPos] = dispatcher
|
||||
|
||||
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
var disp = copySym(s, nextSymId(idgen))
|
||||
proc createDispatcher(s: PSym): PSym =
|
||||
var disp = copySym(s)
|
||||
incl(disp.flags, sfDispatcher)
|
||||
excl(disp.flags, sfExported)
|
||||
let old = disp.typ
|
||||
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
|
||||
copyTypeProps(g, idgen.module, disp.typ, old)
|
||||
|
||||
disp.typ = copyType(disp.typ, disp.typ.owner, false)
|
||||
# we can't inline the dispatcher itself (for now):
|
||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
|
||||
disp.ast = copyTree(s.ast)
|
||||
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||
disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||
disp.loc.r = nil
|
||||
if s.typ[0] != nil:
|
||||
if s.typ.sons[0] != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
||||
disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
|
||||
else:
|
||||
# We've encountered a method prototype without a filled-in
|
||||
# resultPos slot. We put a placeholder in there that will
|
||||
# be updated in fixupDispatcher().
|
||||
disp.ast.add newNodeI(nkEmpty, s.info)
|
||||
disp.ast.addSon(newNodeI(nkEmpty, s.info))
|
||||
attachDispatcher(s, newSymNode(disp))
|
||||
# attach to itself to prevent bugs:
|
||||
attachDispatcher(disp, newSymNode(disp))
|
||||
@@ -141,8 +137,8 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||
# the lock level of the dispatcher needs to be updated/checked
|
||||
# against that of the method.
|
||||
if disp.ast.len > resultPos and meth.ast.len > resultPos and
|
||||
disp.ast[resultPos].kind == nkEmpty:
|
||||
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
|
||||
disp.ast.sons[resultPos].kind == nkEmpty:
|
||||
disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
|
||||
|
||||
# The following code works only with lock levels, so we disable
|
||||
# it when they're not available.
|
||||
@@ -161,13 +157,14 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||
if disp.typ.lockLevel < meth.typ.lockLevel:
|
||||
disp.typ.lockLevel = meth.typ.lockLevel
|
||||
|
||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
|
||||
let L = len(g.methods)
|
||||
var witness: PSym
|
||||
for i in 0..<g.methods.len:
|
||||
for i in 0 ..< L:
|
||||
let disp = g.methods[i].dispatcher
|
||||
case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
|
||||
of Yes:
|
||||
g.methods[i].methods.add(s)
|
||||
add(g.methods[i].methods, s)
|
||||
attachDispatcher(s, disp.ast[dispatcherPos])
|
||||
fixupDispatcher(s, disp, g.config)
|
||||
#echo "fixup ", disp.name.s, " ", disp.id
|
||||
@@ -181,8 +178,10 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
of Invalid:
|
||||
if witness.isNil: witness = g.methods[i].methods[0]
|
||||
# create a new dispatcher:
|
||||
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
|
||||
add(g.methods, (methods: @[s], dispatcher: createDispatcher(s)))
|
||||
#echo "adding ", s.info
|
||||
#if fromCache:
|
||||
# internalError(s.info, "no method dispatcher found")
|
||||
if witness != nil:
|
||||
localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
|
||||
"' to method defined here: " & g.config$witness.info)
|
||||
@@ -191,32 +190,32 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
|
||||
proc relevantCol(methods: seq[PSym], col: int): bool =
|
||||
# returns true iff the position is relevant
|
||||
var t = methods[0].typ[col].skipTypes(skipPtrs)
|
||||
var t = methods[0].typ.sons[col].skipTypes(skipPtrs)
|
||||
if t.kind == tyObject:
|
||||
for i in 1..high(methods):
|
||||
let t2 = skipTypes(methods[i].typ[col], skipPtrs)
|
||||
for i in 1 .. high(methods):
|
||||
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
|
||||
if not sameType(t2, t):
|
||||
return true
|
||||
|
||||
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
||||
for col in 1..<a.typ.len:
|
||||
for col in 1 ..< len(a.typ):
|
||||
if contains(relevantCols, col):
|
||||
var aa = skipTypes(a.typ[col], skipPtrs)
|
||||
var bb = skipTypes(b.typ[col], skipPtrs)
|
||||
var aa = skipTypes(a.typ.sons[col], skipPtrs)
|
||||
var bb = skipTypes(b.typ.sons[col], skipPtrs)
|
||||
var d = inheritanceDiff(aa, bb)
|
||||
if (d != high(int)) and d != 0:
|
||||
return d
|
||||
|
||||
proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||
# we use shellsort here; fast and simple
|
||||
var n = a.len
|
||||
var n = len(a)
|
||||
var h = 1
|
||||
while true:
|
||||
h = 3 * h + 1
|
||||
if h > n: break
|
||||
while true:
|
||||
h = h div 3
|
||||
for i in h..<n:
|
||||
for i in h ..< n:
|
||||
var v = a[i]
|
||||
var j = i
|
||||
while cmpSignatures(a[j - h], v, relevantCols) >= 0:
|
||||
@@ -229,70 +228,71 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
|
||||
var base = methods[0].ast[dispatcherPos].sym
|
||||
result = base
|
||||
var paramLen = base.typ.len
|
||||
var paramLen = len(base.typ)
|
||||
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 1..<paramLen:
|
||||
var ands = getSysMagic(g, unknownLineInfo(), "and", mAnd)
|
||||
var iss = getSysMagic(g, unknownLineInfo(), "of", mOf)
|
||||
let boolType = getSysType(g, unknownLineInfo(), tyBool)
|
||||
for col in 1 ..< paramLen:
|
||||
if contains(relevantCols, col):
|
||||
let param = base.typ.n[col].sym
|
||||
let param = base.typ.n.sons[col].sym
|
||||
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
||||
nilchecks.add newTree(nkCall,
|
||||
newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param))
|
||||
for meth in 0..high(methods):
|
||||
addSon(nilchecks, newTree(nkCall,
|
||||
newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param)))
|
||||
for meth in 0 .. high(methods):
|
||||
var curr = methods[meth] # generate condition:
|
||||
var cond: PNode = nil
|
||||
for col in 1..<paramLen:
|
||||
for col in 1 ..< paramLen:
|
||||
if contains(relevantCols, col):
|
||||
var isn = newNodeIT(nkCall, base.info, boolType)
|
||||
isn.add newSymNode(iss)
|
||||
let param = base.typ.n[col].sym
|
||||
isn.add newSymNode(param)
|
||||
isn.add newNodeIT(nkType, base.info, curr.typ[col])
|
||||
addSon(isn, newSymNode(iss))
|
||||
let param = base.typ.n.sons[col].sym
|
||||
addSon(isn, newSymNode(param))
|
||||
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
||||
if cond != nil:
|
||||
var a = newNodeIT(nkCall, base.info, boolType)
|
||||
a.add newSymNode(ands)
|
||||
a.add cond
|
||||
a.add isn
|
||||
addSon(a, newSymNode(ands))
|
||||
addSon(a, cond)
|
||||
addSon(a, isn)
|
||||
cond = a
|
||||
else:
|
||||
cond = isn
|
||||
let retTyp = base.typ[0]
|
||||
let retTyp = base.typ.sons[0]
|
||||
let call = newNodeIT(nkCall, base.info, retTyp)
|
||||
call.add newSymNode(curr)
|
||||
for col in 1..<paramLen:
|
||||
call.add genConv(newSymNode(base.typ.n[col].sym),
|
||||
curr.typ[col], false, g.config)
|
||||
addSon(call, newSymNode(curr))
|
||||
for col in 1 ..< paramLen:
|
||||
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
|
||||
curr.typ.sons[col], false, g.config))
|
||||
var ret: PNode
|
||||
if retTyp != nil:
|
||||
var a = newNodeI(nkFastAsgn, base.info)
|
||||
a.add newSymNode(base.ast[resultPos].sym)
|
||||
a.add call
|
||||
addSon(a, newSymNode(base.ast.sons[resultPos].sym))
|
||||
addSon(a, call)
|
||||
ret = newNodeI(nkReturnStmt, base.info)
|
||||
ret.add a
|
||||
addSon(ret, a)
|
||||
else:
|
||||
ret = call
|
||||
if cond != nil:
|
||||
var a = newNodeI(nkElifBranch, base.info)
|
||||
a.add cond
|
||||
a.add ret
|
||||
disp.add a
|
||||
addSon(a, cond)
|
||||
addSon(a, ret)
|
||||
addSon(disp, a)
|
||||
else:
|
||||
disp = ret
|
||||
nilchecks.add disp
|
||||
nilchecks.flags.incl nfTransf # should not be further transformed
|
||||
result.ast[bodyPos] = nilchecks
|
||||
result.ast.sons[bodyPos] = nilchecks
|
||||
|
||||
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
for bucket in 0..<g.methods.len:
|
||||
for bucket in 0 ..< len(g.methods):
|
||||
var relevantCols = initIntSet()
|
||||
for col in 1..<g.methods[bucket].methods[0].typ.len:
|
||||
for col in 1 ..< len(g.methods[bucket].methods[0].typ):
|
||||
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)
|
||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))
|
||||
addSon(result,
|
||||
newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols)))
|
||||
|
||||
@@ -121,10 +121,7 @@
|
||||
# yield 2
|
||||
# if :unrollFinally: # This node is created by `newEndFinallyNode`
|
||||
# if :curExc.isNil:
|
||||
# if nearestFinally == 0:
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# :state = nearestFinally # bubble up
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# closureIterSetupExc(nil)
|
||||
# raise
|
||||
@@ -133,8 +130,7 @@
|
||||
|
||||
import
|
||||
ast, msgs, idents,
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
|
||||
tables, options
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
|
||||
|
||||
type
|
||||
Ctx = object
|
||||
@@ -156,11 +152,10 @@ type
|
||||
curExcHandlingState: int # Negative for except, positive for finally
|
||||
nearestFinally: int # Index of the nearest finally block. For try/except it
|
||||
# is their finally. For finally it is parent finally. Otherwise -1
|
||||
idgen: IdGenerator
|
||||
|
||||
const
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
|
||||
nkSkip = { nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt } + procDefs
|
||||
|
||||
proc newStateAccess(ctx: var Ctx): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
@@ -180,7 +175,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
|
||||
ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
|
||||
|
||||
proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info)
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.fn, ctx.fn.info)
|
||||
result.typ = typ
|
||||
assert(not typ.isNil)
|
||||
|
||||
@@ -193,7 +188,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
|
||||
else:
|
||||
let envParam = getEnvParam(ctx.fn)
|
||||
# let obj = envParam.typ.lastSon
|
||||
result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache, ctx.idgen)
|
||||
result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache)
|
||||
|
||||
proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
@@ -242,7 +237,7 @@ proc toStmtList(n: PNode): PNode =
|
||||
proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
|
||||
# Make sure `n` is a stmtlist, and ends with `gotoOut`
|
||||
result = toStmtList(n)
|
||||
if result.len == 0 or result[^1].kind != nkGotoState:
|
||||
if result.len == 0 or result.sons[^1].kind != nkGotoState:
|
||||
result.add(gotoOut)
|
||||
|
||||
proc newTempVar(ctx: var Ctx, typ: PType): PSym =
|
||||
@@ -278,7 +273,7 @@ proc transformBreaksAndContinuesInWhile(ctx: var Ctx, n: PNode, before, after: P
|
||||
if ctx.blockLevel == 0:
|
||||
result = after
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformBreaksAndContinuesInWhile(n[i], before, after)
|
||||
|
||||
proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode =
|
||||
@@ -298,7 +293,7 @@ proc transformBreaksInBlock(ctx: var Ctx, n: PNode, label, after: PNode): PNode
|
||||
if label.kind == nkSym and n[0].sym == label.sym:
|
||||
result = after
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformBreaksInBlock(n[i], label, after)
|
||||
|
||||
proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
@@ -323,7 +318,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
|
||||
|
||||
if c.len > 1:
|
||||
var cond: PNode
|
||||
for i in 0..<c.len - 1:
|
||||
for i in 0 .. c.len - 2:
|
||||
assert(c[i].kind == nkType)
|
||||
let nextCond = newTree(nkCall,
|
||||
newSymNode(g.getSysMagic(c.info, "of", mOf)),
|
||||
@@ -393,10 +388,6 @@ proc hasYieldsInExpressions(n: PNode): bool =
|
||||
return true
|
||||
else:
|
||||
result = n.hasYields
|
||||
of nkCast:
|
||||
for i in 1..<n.len:
|
||||
if n[i].hasYieldsInExpressions:
|
||||
return true
|
||||
else:
|
||||
for c in n:
|
||||
if c.hasYieldsInExpressions:
|
||||
@@ -410,18 +401,15 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
|
||||
var n = n
|
||||
while n.kind == nkStmtListExpr:
|
||||
result.s.sons.add(n.sons)
|
||||
result.s.sons.setLen(result.s.len - 1) # delete last son
|
||||
result.s.sons.setLen(result.s.sons.len - 1) # delete last son
|
||||
n = n[^1]
|
||||
|
||||
result.res = n
|
||||
|
||||
|
||||
proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
|
||||
if isEmptyType(v.typ):
|
||||
result = v
|
||||
else:
|
||||
result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
|
||||
result.info = v.info
|
||||
result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
|
||||
result.info = v.info
|
||||
|
||||
proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
|
||||
if input.kind == nkStmtListExpr:
|
||||
@@ -447,7 +435,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkYieldStmt:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -461,7 +449,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkPar, nkObjConstr, nkTupleConstr, nkBracket:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -471,7 +459,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil")
|
||||
result.typ = n.typ
|
||||
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
case n[i].kind
|
||||
of nkExprColonExpr:
|
||||
if n[i][1].kind == nkStmtListExpr:
|
||||
@@ -487,12 +475,13 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkIfStmt, nkIfExpr:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
needsSplit = true
|
||||
var tmp: PSym
|
||||
var s: PNode
|
||||
let isExpr = not isEmptyType(n.typ)
|
||||
if isExpr:
|
||||
tmp = ctx.newTempVar(n.typ)
|
||||
@@ -552,7 +541,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkTryStmt, nkHiddenTryStmt:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -565,7 +554,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
let tmp = ctx.newTempVar(n.typ)
|
||||
|
||||
n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
|
||||
for i in 1..<n.len:
|
||||
for i in 1 ..< n.len:
|
||||
let branch = n[i]
|
||||
case branch.kind
|
||||
of nkExceptBranch:
|
||||
@@ -582,7 +571,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkCaseStmt:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -600,7 +589,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
result.add(st)
|
||||
n[0] = ex
|
||||
|
||||
for i in 1..<n.len:
|
||||
for i in 1 ..< n.len:
|
||||
let branch = n[i]
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
@@ -611,16 +600,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
|
||||
result.add(n)
|
||||
result.add(ctx.newEnvVarAccess(tmp))
|
||||
elif n[0].kind == nkStmtListExpr:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
let (st, ex) = exprToStmtList(n[0])
|
||||
result.add(st)
|
||||
n[0] = ex
|
||||
result.add(n)
|
||||
|
||||
of nkCallKinds, nkChckRange, nkChckRangeF, nkChckRange64:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -660,7 +643,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
result.add(ifNode)
|
||||
result.add(ctx.newEnvVarAccess(tmp))
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].kind == nkStmtListExpr:
|
||||
let (st, ex) = exprToStmtList(n[i])
|
||||
result.add(st)
|
||||
@@ -689,7 +672,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkDiscardStmt, nkReturnStmt, nkRaiseStmt:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -703,7 +686,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv,
|
||||
nkDerefExpr, nkHiddenDeref:
|
||||
var ns = false
|
||||
for i in ord(n.kind == nkCast)..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -717,7 +700,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
of nkAsgn, nkFastAsgn:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
|
||||
if ns:
|
||||
@@ -755,6 +738,8 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
result.add(n)
|
||||
|
||||
of nkWhileStmt:
|
||||
var ns = false
|
||||
|
||||
var condNeedsSplit = false
|
||||
n[0] = ctx.lowerStmtListExprs(n[0], condNeedsSplit)
|
||||
var bodyNeedsSplit = false
|
||||
@@ -793,24 +778,21 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
let (st, ex) = exprToStmtList(n[1])
|
||||
n.transitionSonsKind(nkBlockStmt)
|
||||
n.kind = nkBlockStmt
|
||||
n.typ = nil
|
||||
n[1] = st
|
||||
result.add(n)
|
||||
result.add(ex)
|
||||
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], needsSplit)
|
||||
|
||||
proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
# Generate the following code:
|
||||
# if :unrollFinally:
|
||||
# if :curExc.isNil:
|
||||
# if nearestFinally == 0:
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# :state = nearestFinally # bubble up
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# raise
|
||||
let curExc = ctx.newCurExcAccess()
|
||||
@@ -819,20 +801,11 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
|
||||
cmp.typ = ctx.g.getSysType(info, tyBool)
|
||||
|
||||
let retStmt =
|
||||
if ctx.nearestFinally == 0:
|
||||
# last finally, we can return
|
||||
let retValue = if ctx.fn.typ[0].isNil:
|
||||
ctx.g.emptyNode
|
||||
else:
|
||||
newTree(nkFastAsgn,
|
||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
|
||||
ctx.newTmpResultAccess())
|
||||
newTree(nkReturnStmt, retValue)
|
||||
else:
|
||||
# bubble up to next finally
|
||||
newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally))
|
||||
let asgn = newTree(nkFastAsgn,
|
||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
|
||||
ctx.newTmpResultAccess())
|
||||
|
||||
let retStmt = newTree(nkReturnStmt, asgn)
|
||||
let branch = newTree(nkElifBranch, cmp, retStmt)
|
||||
|
||||
let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
|
||||
@@ -865,8 +838,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
if n[0].kind != nkEmpty:
|
||||
let asgnTmpResult = newNodeI(nkAsgn, n.info)
|
||||
asgnTmpResult.add(ctx.newTmpResultAccess())
|
||||
let x = if n[0].kind in {nkAsgn, nkFastAsgn}: n[0][1] else: n[0]
|
||||
asgnTmpResult.add(x)
|
||||
asgnTmpResult.add(n[0])
|
||||
result.add(asgnTmpResult)
|
||||
|
||||
result.add(ctx.newNullifyCurExc(n.info))
|
||||
@@ -876,15 +848,8 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
|
||||
of nkSkip:
|
||||
discard
|
||||
of nkTryStmt:
|
||||
if n.hasYields:
|
||||
# the inner try will handle these transformations
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
n[i] = ctx.transformReturnsInTry(n[i])
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformReturnsInTry(n[i])
|
||||
|
||||
proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode =
|
||||
@@ -894,14 +859,14 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
result = addGotoOut(result, gotoOut)
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].hasYields:
|
||||
# Create a new split
|
||||
let go = newNodeI(nkGotoState, n[i].info)
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], go)
|
||||
|
||||
let s = newNodeI(nkStmtList, n[i + 1].info)
|
||||
for j in i + 1..<n.len:
|
||||
for j in i + 1 ..< n.len:
|
||||
s.add(n[j])
|
||||
|
||||
n.sons.setLen(i + 1)
|
||||
@@ -924,7 +889,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
result[^1] = ctx.transformClosureIteratorBody(result[^1], gotoOut)
|
||||
|
||||
of nkIfStmt, nkCaseStmt:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||
if n[^1].kind != nkElse:
|
||||
# We don't have an else branch, but every possible branch has to end with
|
||||
@@ -1036,7 +1001,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
internalError(ctx.g.config, "closure iter " & $n.kind)
|
||||
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||
|
||||
proc stateFromGotoState(n: PNode): int =
|
||||
@@ -1073,18 +1038,18 @@ proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode =
|
||||
result.add(ctx.newStateAssgn(stateFromGotoState(n[1])))
|
||||
|
||||
var retStmt = newNodeI(nkReturnStmt, n.info)
|
||||
if n[0][0].kind != nkEmpty:
|
||||
var a = newNodeI(nkAsgn, n[0][0].info)
|
||||
var retVal = n[0][0] #liftCapturedVars(n[0], owner, d, c)
|
||||
a.add newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen))
|
||||
a.add retVal
|
||||
if n[0].sons[0].kind != nkEmpty:
|
||||
var a = newNodeI(nkAsgn, n[0].sons[0].info)
|
||||
var retVal = n[0].sons[0] #liftCapturedVars(n.sons[0], owner, d, c)
|
||||
addSon(a, newSymNode(getClosureIterResult(ctx.g, ctx.fn)))
|
||||
addSon(a, retVal)
|
||||
retStmt.add(a)
|
||||
else:
|
||||
retStmt.add(ctx.g.emptyNode)
|
||||
|
||||
result.add(retStmt)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformStateAssignments(n[i])
|
||||
|
||||
of nkSkip:
|
||||
@@ -1104,7 +1069,7 @@ proc transformStateAssignments(ctx: var Ctx, n: PNode): PNode =
|
||||
result.add(breakState)
|
||||
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformStateAssignments(n[i])
|
||||
|
||||
proc skipStmtList(ctx: Ctx; n: PNode): PNode =
|
||||
@@ -1136,7 +1101,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
|
||||
|
||||
result = ctx.states[stateIdx][0].intVal.int
|
||||
|
||||
proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
|
||||
proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode =
|
||||
result = n
|
||||
case n.kind
|
||||
of nkSkip:
|
||||
@@ -1145,14 +1110,14 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
|
||||
result = copyTree(n)
|
||||
result[0].intVal = ctx.skipEmptyStates(result[0].intVal.int)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.skipThroughEmptyStates(n[i])
|
||||
|
||||
proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: PSym): PType =
|
||||
result = newType(tyArray, nextTypeId(idgen), owner)
|
||||
proc newArrayType(g: ModuleGraph; n: int, t: PType, owner: PSym): PType =
|
||||
result = newType(tyArray, owner)
|
||||
|
||||
let rng = newType(tyRange, nextTypeId(idgen), owner)
|
||||
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n - 1))
|
||||
let rng = newType(tyRange, owner)
|
||||
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
|
||||
rng.rawAddSon(t)
|
||||
|
||||
result.rawAddSon(rng)
|
||||
@@ -1160,7 +1125,7 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: P
|
||||
|
||||
proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
|
||||
result = newNodeI(nkBracket, ctx.fn.info)
|
||||
result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
|
||||
result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.fn)
|
||||
|
||||
for i in ctx.exceptionTable:
|
||||
let elem = newIntNode(nkIntLit, i)
|
||||
@@ -1316,125 +1281,19 @@ proc deleteEmptyStates(ctx: var Ctx) =
|
||||
else:
|
||||
inc i
|
||||
|
||||
type
|
||||
PreprocessContext = object
|
||||
finallys: seq[PNode]
|
||||
config: ConfigRef
|
||||
blocks: seq[(PNode, int)]
|
||||
idgen: IdGenerator
|
||||
FreshVarsContext = object
|
||||
tab: Table[int, PSym]
|
||||
config: ConfigRef
|
||||
info: TLineInfo
|
||||
idgen: IdGenerator
|
||||
|
||||
proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
let x = c.tab.getOrDefault(n.sym.id)
|
||||
if x == nil:
|
||||
result = n
|
||||
else:
|
||||
result = newSymNode(x, n.info)
|
||||
of nkSkip - {nkSym}:
|
||||
result = n
|
||||
of nkLetSection, nkVarSection:
|
||||
result = copyNode(n)
|
||||
for it in n:
|
||||
if it.kind in {nkIdentDefs, nkVarTuple}:
|
||||
let idefs = copyNode(it)
|
||||
for v in 0..it.len-3:
|
||||
if it[v].kind == nkSym:
|
||||
let x = copySym(it[v].sym, nextSymId(c.idgen))
|
||||
c.tab[it[v].sym.id] = x
|
||||
idefs.add newSymNode(x)
|
||||
else:
|
||||
idefs.add it[v]
|
||||
|
||||
for rest in it.len-2 ..< it.len: idefs.add it[rest]
|
||||
result.add idefs
|
||||
else:
|
||||
result.add it
|
||||
of nkRaiseStmt:
|
||||
localError(c.config, c.info, "unsupported control flow: 'finally: ... raise' duplicated because of 'break'")
|
||||
else:
|
||||
result = n
|
||||
for i in 0..<n.safeLen:
|
||||
result[i] = freshVars(n[i], c)
|
||||
|
||||
proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||
# in order to fix bug #15243 without risking regressions, we preprocess
|
||||
# the AST so that 'break' statements inside a 'try finally' also have the
|
||||
# finally section. We need to duplicate local variables here and also
|
||||
# detect: 'finally: raises X' which is currently not supported. We produce
|
||||
# an error for this case for now. All this will be done properly with Yuriy's
|
||||
# patch.
|
||||
|
||||
result = n
|
||||
case n.kind
|
||||
of nkTryStmt:
|
||||
let f = n.lastSon
|
||||
var didAddSomething = false
|
||||
if f.kind == nkFinally:
|
||||
c.finallys.add f.lastSon
|
||||
didAddSomething = true
|
||||
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
|
||||
if didAddSomething:
|
||||
discard c.finallys.pop()
|
||||
|
||||
of nkWhileStmt, nkBlockStmt:
|
||||
if n.hasYields == false: return n
|
||||
c.blocks.add((n, c.finallys.len))
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
discard c.blocks.pop()
|
||||
|
||||
of nkBreakStmt:
|
||||
if c.blocks.len == 0:
|
||||
discard
|
||||
else:
|
||||
var fin = -1
|
||||
if n[0].kind == nkEmpty:
|
||||
fin = c.blocks[^1][1]
|
||||
elif n[0].kind == nkSym:
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if c.blocks[i][0].kind == nkBlockStmt and c.blocks[i][0][0].kind == nkSym and
|
||||
c.blocks[i][0][0].sym == n[0].sym:
|
||||
fin = c.blocks[i][1]
|
||||
break
|
||||
|
||||
if fin >= 0:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
for i in countdown(c.finallys.high, fin):
|
||||
var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
|
||||
result.add freshVars(copyTree(c.finallys[i]), vars)
|
||||
c.idgen = vars.idgen
|
||||
result.add n
|
||||
of nkSkip: discard
|
||||
else:
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
|
||||
proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
|
||||
proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
|
||||
var ctx: Ctx
|
||||
ctx.g = g
|
||||
ctx.fn = fn
|
||||
ctx.idgen = idgen
|
||||
|
||||
if getEnvParam(fn).isNil:
|
||||
# Lambda lifting was not done yet. Use temporary :state sym, which will
|
||||
# be handled specially by lambda lifting. Local temp vars (if needed)
|
||||
# should follow the same logic.
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info)
|
||||
var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
|
||||
var n = preprocess(pc, n.toStmtList)
|
||||
#echo "transformed into ", n
|
||||
#var n = n.toStmtList
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
|
||||
var n = n.toStmtList
|
||||
|
||||
discard ctx.newState(n, nil)
|
||||
let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
|
||||
|
||||
@@ -7,31 +7,20 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
|
||||
## Helpers for binaries that use compiler passes, eg: nim, nimsuggest, nimfix
|
||||
|
||||
import
|
||||
options, idents, nimconf, extccomp, commands, msgs,
|
||||
lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
|
||||
options, idents, nimconf, scriptconfig, extccomp, commands, msgs,
|
||||
lineinfos, modulegraphs, condsyms, os, pathutils
|
||||
|
||||
proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
|
||||
when defined(unix):
|
||||
if os.isAbsolute(f.string): result = f
|
||||
else: result = AbsoluteFile("./" & f.string)
|
||||
else:
|
||||
result = f
|
||||
|
||||
proc addCmdPrefix*(result: var string, kind: CmdLineKind) =
|
||||
# consider moving this to std/parseopt
|
||||
case kind
|
||||
of cmdLongOption: result.add "--"
|
||||
of cmdShortOption: result.add "-"
|
||||
of cmdArgument, cmdEnd: discard
|
||||
from strutils import normalize
|
||||
|
||||
type
|
||||
NimProg* = ref object
|
||||
suggestMode*: bool
|
||||
supportsStdinFile*: bool
|
||||
processCmdLine*: proc(pass: TCmdLinePass, cmd: string; config: ConfigRef)
|
||||
mainCommand*: proc(graph: ModuleGraph)
|
||||
|
||||
proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =
|
||||
condsyms.initDefines(conf.symbols)
|
||||
@@ -39,40 +28,69 @@ proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =
|
||||
|
||||
proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
|
||||
self.processCmdLine(passCmd1, "", conf)
|
||||
if conf.projectIsCmd and conf.projectName in ["-", ""]:
|
||||
handleCmdInput(conf)
|
||||
elif self.supportsStdinFile and conf.projectName == "-":
|
||||
if self.supportsStdinFile and conf.projectName == "-":
|
||||
handleStdinInput(conf)
|
||||
elif conf.projectName != "":
|
||||
setFromProjectName(conf, conf.projectName)
|
||||
try:
|
||||
conf.projectFull = canonicalizePath(conf, AbsoluteFile conf.projectName)
|
||||
except OSError:
|
||||
conf.projectFull = AbsoluteFile conf.projectName
|
||||
let p = splitFile(conf.projectFull)
|
||||
let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir
|
||||
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
|
||||
conf.projectName = p.name
|
||||
else:
|
||||
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
|
||||
|
||||
proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef;
|
||||
graph: ModuleGraph): bool =
|
||||
proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool =
|
||||
loadConfigs(DefaultConfig, cache, conf) # load all config files
|
||||
if self.suggestMode:
|
||||
conf.setCmd cmdIdeTools
|
||||
if conf.cmd == cmdNimscript:
|
||||
incl(conf.globalOptions, optWasNimscript)
|
||||
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
|
||||
conf.command = "nimsuggest"
|
||||
|
||||
if not self.suggestMode:
|
||||
template runNimScriptIfExists(path: AbsoluteFile) =
|
||||
let p = path # eval once
|
||||
if fileExists(p):
|
||||
runNimScript(cache, p, freshDefines = false, conf)
|
||||
|
||||
# Caution: make sure this stays in sync with `loadConfigs`
|
||||
if optSkipSystemConfigFile notin conf.globalOptions:
|
||||
runNimScriptIfExists(getSystemConfigPath(conf, DefaultConfigNims))
|
||||
|
||||
if optSkipUserConfigFile notin conf.globalOptions:
|
||||
runNimScriptIfExists(getUserConfigPath(DefaultConfigNims))
|
||||
|
||||
if optSkipParentConfigFiles notin conf.globalOptions:
|
||||
for dir in parentDirs(conf.projectPath.string, fromRoot = true, inclusive = false):
|
||||
runNimScriptIfExists(AbsoluteDir(dir) / DefaultConfigNims)
|
||||
|
||||
if optSkipProjConfigFile notin conf.globalOptions:
|
||||
runNimScriptIfExists(conf.projectPath / DefaultConfigNims)
|
||||
block:
|
||||
let scriptFile = conf.projectFull.changeFileExt("nims")
|
||||
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
|
||||
if fileExists(scriptFile) and scriptFile == conf.projectFull:
|
||||
if conf.cmd == cmdNone: conf.setCmd cmdNimscript
|
||||
if conf.cmd == cmdNimscript: return false
|
||||
if not self.suggestMode:
|
||||
runNimScriptIfExists(scriptFile)
|
||||
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
|
||||
if fileExists(scriptFile) and scriptFile == conf.projectFull:
|
||||
if conf.command == "":
|
||||
conf.command = "e"
|
||||
return false
|
||||
elif conf.command.normalize == "e":
|
||||
return false
|
||||
else:
|
||||
if scriptFile != conf.projectFull:
|
||||
runNimScriptIfExists(scriptFile)
|
||||
else:
|
||||
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file
|
||||
discard
|
||||
|
||||
# now process command line arguments again, because some options in the
|
||||
# command line can overwrite the config file's settings
|
||||
if conf.backend != backendJs: # bug #19059
|
||||
extccomp.initVars(conf)
|
||||
extccomp.initVars(conf)
|
||||
self.processCmdLine(passCmd2, "", conf)
|
||||
if conf.cmd == cmdNone:
|
||||
if conf.command == "":
|
||||
rawMessage(conf, errGenerated, "command missing")
|
||||
|
||||
let graph = newModuleGraph(cache, conf)
|
||||
graph.suggestMode = self.suggestMode
|
||||
self.mainCommand(graph)
|
||||
return true
|
||||
|
||||
proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool =
|
||||
## Alias for loadConfigsAndProcessCmdLine, here for backwards compatibility
|
||||
loadConfigsAndProcessCmdLine(self, cache, conf, graph)
|
||||
|
||||
@@ -9,30 +9,34 @@
|
||||
|
||||
# This module handles the parsing of command line arguments.
|
||||
|
||||
|
||||
# We do this here before the 'import' statement so 'defined' does not get
|
||||
# confused with 'TGCMode.gcMarkAndSweep' etc.
|
||||
# confused with 'TGCMode.gcGenerational' etc.
|
||||
template bootSwitch(name, expr, userString) =
|
||||
# Helper to build boot constants, for debugging you can 'echo' the else part.
|
||||
const name = if expr: " " & userString else: ""
|
||||
|
||||
bootSwitch(usedRelease, defined(release), "-d:release")
|
||||
bootSwitch(usedDanger, defined(danger), "-d:danger")
|
||||
# `useLinenoise` deprecated in favor of `nimUseLinenoise`, kept for backward compatibility
|
||||
bootSwitch(useLinenoise, defined(nimUseLinenoise) or defined(useLinenoise), "-d:nimUseLinenoise")
|
||||
bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
|
||||
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
|
||||
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
|
||||
bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
|
||||
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
|
||||
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
|
||||
|
||||
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
|
||||
import
|
||||
msgs, options, nversion, condsyms, extccomp, platform,
|
||||
wordrecg, nimblecmd, lineinfos, pathutils, pathnorm
|
||||
os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
|
||||
wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
|
||||
pathutils, strtabs
|
||||
|
||||
from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
|
||||
from incremental import nimIncremental
|
||||
|
||||
# but some have deps to imported modules. Yay.
|
||||
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
||||
bootSwitch(usedNativeStacktrace,
|
||||
defined(nativeStackTrace) and nativeStackTraceSupported,
|
||||
"-d:nativeStackTrace")
|
||||
bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI")
|
||||
|
||||
type
|
||||
@@ -47,14 +51,15 @@ const
|
||||
"Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
|
||||
|
||||
proc genFeatureDesc[T: enum](t: typedesc[T]): string {.compileTime.} =
|
||||
result = ""
|
||||
for f in T:
|
||||
if result.len > 0: result.add "|"
|
||||
result.add $f
|
||||
var x = ""
|
||||
for f in low(T)..high(T):
|
||||
if x.len > 0: x.add "|"
|
||||
x.add $f
|
||||
x
|
||||
|
||||
const
|
||||
Usage = slurp"../doc/basicopt.txt".replace(" //", " ")
|
||||
AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)]
|
||||
Usage = slurp"../doc/basicopt.txt".replace(" //", " ")
|
||||
AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)]
|
||||
|
||||
proc getCommandLineDesc(conf: ConfigRef): string =
|
||||
result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name,
|
||||
@@ -91,14 +96,13 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
|
||||
CPU[conf.target.hostCPU].name, CompileDate]),
|
||||
{msgStdout})
|
||||
|
||||
const gitHash {.strdefine.} = gorge("git log -n 1 --format=%H").strip
|
||||
# xxx move this logic to std/private/gitutils
|
||||
const gitHash = gorge("git log -n 1 --format=%H").strip
|
||||
when gitHash.len == 40:
|
||||
msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
|
||||
|
||||
msgWriteln(conf, "active boot switches:" & usedRelease & usedDanger &
|
||||
usedTinyC & useLinenoise &
|
||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGoGC & usedNoGC,
|
||||
usedTinyC & usedGnuReadline & usedNativeStacktrace &
|
||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
|
||||
{msgStdout})
|
||||
msgQuit(0)
|
||||
|
||||
@@ -106,14 +110,14 @@ proc writeCommandLineUsage*(conf: ConfigRef) =
|
||||
msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
|
||||
|
||||
proc addPrefix(switch: string): string =
|
||||
if switch.len <= 1: result = "-" & switch
|
||||
if len(switch) <= 1: result = "-" & switch
|
||||
else: result = "--" & switch
|
||||
|
||||
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', 'error' or 'usages' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint' or 'error' expected, but '$1' found"
|
||||
|
||||
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
|
||||
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
|
||||
@@ -123,50 +127,41 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC
|
||||
info: TLineInfo) =
|
||||
cmd = ""
|
||||
var i = 0
|
||||
if i < switch.len and switch[i] == '-': inc(i)
|
||||
if i < switch.len and switch[i] == '-': inc(i)
|
||||
while i < switch.len:
|
||||
if i < len(switch) and switch[i] == '-': inc(i)
|
||||
if i < len(switch) and switch[i] == '-': inc(i)
|
||||
while i < len(switch):
|
||||
case switch[i]
|
||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': cmd.add(switch[i])
|
||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i])
|
||||
else: break
|
||||
inc(i)
|
||||
if i >= switch.len: arg = ""
|
||||
if i >= len(switch): arg = ""
|
||||
# cmd:arg => (cmd,arg)
|
||||
elif switch[i] in {':', '='}: arg = substr(switch, i + 1)
|
||||
# cmd[sub]:rest => (cmd,[sub]:rest)
|
||||
elif switch[i] == '[': arg = substr(switch, i)
|
||||
else: invalidCmdLineOption(conf, pass, switch, info)
|
||||
|
||||
template switchOn(arg: string): bool =
|
||||
# xxx use `switchOn` wherever appropriate
|
||||
case arg.normalize
|
||||
of "", "on": true
|
||||
of "off": false
|
||||
else:
|
||||
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
false
|
||||
|
||||
proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
|
||||
info: TLineInfo) =
|
||||
case arg.normalize
|
||||
of "", "on": conf.options.incl op
|
||||
of "off": conf.options.excl op
|
||||
of "","on": conf.options = conf.options + op
|
||||
of "off": conf.options = conf.options - op
|
||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
|
||||
proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
|
||||
info: TLineInfo): bool =
|
||||
result = false
|
||||
case arg.normalize
|
||||
of "on": conf.options.incl op
|
||||
of "off": conf.options.excl op
|
||||
of "on": conf.options = conf.options + op
|
||||
of "off": conf.options = conf.options - op
|
||||
of "list": result = true
|
||||
else: localError(conf, info, errOnOffOrListExpectedButXFound % arg)
|
||||
|
||||
proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
|
||||
info: TLineInfo) =
|
||||
case arg.normalize
|
||||
of "", "on": conf.globalOptions.incl op
|
||||
of "off": conf.globalOptions.excl op
|
||||
of "", "on": conf.globalOptions = conf.globalOptions + op
|
||||
of "off": conf.globalOptions = conf.globalOptions - op
|
||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
|
||||
proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
@@ -179,55 +174,41 @@ proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info:
|
||||
|
||||
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 id = "" # arg = key:val or [key]:val; with val=on|off
|
||||
var i = 0
|
||||
var notes: set[TMsgKind]
|
||||
var n = hintMin
|
||||
var isBracket = false
|
||||
if i < arg.len and arg[i] == '[':
|
||||
if i < len(arg) and arg[i] == '[':
|
||||
isBracket = true
|
||||
inc(i)
|
||||
while i < arg.len and (arg[i] notin {':', '=', ']'}):
|
||||
id.add(arg[i])
|
||||
while i < len(arg) and (arg[i] notin {':', '=', ']'}):
|
||||
add(id, arg[i])
|
||||
inc(i)
|
||||
if isBracket:
|
||||
if i < arg.len and arg[i] == ']': inc(i)
|
||||
if i < len(arg) and arg[i] == ']': inc(i)
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
|
||||
if i == arg.len: discard
|
||||
elif i < arg.len and (arg[i] in {':', '='}): inc(i)
|
||||
if i < len(arg) and (arg[i] in {':', '='}): inc(i)
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
|
||||
let isSomeHint = state in {wHint, wHintAsError}
|
||||
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: 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}
|
||||
elif isSomeHint: findNote(hintMin, hintMax, "hint")
|
||||
else: findNote(warnMin, warnMax, "warning")
|
||||
var val = substr(arg, i).normalize
|
||||
if val == "": val = "on"
|
||||
if val notin ["on", "off"]:
|
||||
# xxx in future work we should also allow users to have control over `foreignPackageNotes`
|
||||
# so that they can enable `hints|warnings|warningAsErrors` for all the code they depend on.
|
||||
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
if state == wHint:
|
||||
let x = findStr(lineinfos.HintsToStr, id)
|
||||
if x >= 0: n = TNoteKind(x + ord(hintMin))
|
||||
else: localError(conf, info, "unknown hint: " & id)
|
||||
else:
|
||||
let isOn = val == "on"
|
||||
if isOn and id.normalize == "all":
|
||||
localError(conf, info, "only 'all:off' is supported")
|
||||
for n in notes:
|
||||
if n notin conf.cmdlineNotes or pass == passCmd1:
|
||||
if pass == passCmd1: incl(conf.cmdlineNotes, n)
|
||||
incl(conf.modifiedyNotes, n)
|
||||
if state in {wWarningAsError, wHintAsError}:
|
||||
conf.warningAsErrors[n] = isOn # xxx rename warningAsErrors to noteAsErrors
|
||||
else:
|
||||
conf.notes[n] = isOn
|
||||
conf.mainPackageNotes[n] = isOn
|
||||
if not isOn: excl(conf.foreignPackageNotes, n)
|
||||
let x = findStr(lineinfos.WarningsToStr, id)
|
||||
if x >= 0: n = TNoteKind(x + ord(warnMin))
|
||||
else: localError(conf, info, "unknown warning: " & id)
|
||||
case substr(arg, i).normalize
|
||||
of "on":
|
||||
incl(conf.notes, n)
|
||||
incl(conf.mainPackageNotes, n)
|
||||
incl(conf.enableNotes, n)
|
||||
of "off":
|
||||
excl(conf.notes, n)
|
||||
excl(conf.mainPackageNotes, n)
|
||||
incl(conf.disableNotes, n)
|
||||
excl(conf.foreignPackageNotes, n)
|
||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
|
||||
proc processCompile(conf: ConfigRef; filename: string) =
|
||||
var found = findFile(conf, filename)
|
||||
@@ -235,31 +216,23 @@ proc processCompile(conf: ConfigRef; filename: string) =
|
||||
extccomp.addExternalFileToCompile(conf, found)
|
||||
|
||||
const
|
||||
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
||||
errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
|
||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
|
||||
template warningOptionNoop(switch: string) =
|
||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||
|
||||
template deprecatedAlias(oldName, newName: string) =
|
||||
warningDeprecated(conf, info, "'$#' is a deprecated alias for '$#'" % [oldName, newName])
|
||||
|
||||
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
||||
case switch.normalize
|
||||
of "gc", "mm":
|
||||
of "gc":
|
||||
case arg.normalize
|
||||
of "boehm": result = conf.selectedGC == gcBoehm
|
||||
of "refc": result = conf.selectedGC == gcRefc
|
||||
of "boehm": result = conf.selectedGC == gcBoehm
|
||||
of "refc": result = conf.selectedGC == gcRefc
|
||||
of "v2": result = false
|
||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||
of "destructors", "arc": result = conf.selectedGC == gcArc
|
||||
of "orc": result = conf.selectedGC == gcOrc
|
||||
of "hooks": result = conf.selectedGC == gcHooks
|
||||
of "go": result = conf.selectedGC == gcGo
|
||||
of "none": result = conf.selectedGC == gcNone
|
||||
of "generational": result = false
|
||||
of "destructors": result = conf.selectedGC == gcDestructors
|
||||
of "go": result = conf.selectedGC == gcGo
|
||||
of "none": result = conf.selectedGC == gcNone
|
||||
of "stack", "regions": result = conf.selectedGC == gcRegions
|
||||
of "v2", "generational": warningOptionNoop(arg)
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "opt":
|
||||
case arg.normalize
|
||||
@@ -270,22 +243,15 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
of "verbosity": result = $conf.verbosity == arg
|
||||
of "app":
|
||||
case arg.normalize
|
||||
of "gui": result = contains(conf.globalOptions, optGenGuiApp)
|
||||
of "console": result = not contains(conf.globalOptions, optGenGuiApp)
|
||||
of "lib": result = contains(conf.globalOptions, optGenDynLib) and
|
||||
of "gui": result = contains(conf.globalOptions, optGenGuiApp)
|
||||
of "console": result = not contains(conf.globalOptions, optGenGuiApp)
|
||||
of "lib": result = contains(conf.globalOptions, optGenDynLib) and
|
||||
not contains(conf.globalOptions, optGenGuiApp)
|
||||
of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
|
||||
not contains(conf.globalOptions, optGenGuiApp)
|
||||
else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
|
||||
of "dynliboverride":
|
||||
result = isDynlibOverride(conf, arg)
|
||||
of "exceptions":
|
||||
case arg.normalize
|
||||
of "cpp": result = conf.exc == excCpp
|
||||
of "setjmp": result = conf.exc == excSetjmp
|
||||
of "quirky": result = conf.exc == excQuirky
|
||||
of "goto": result = conf.exc == excGoto
|
||||
else: localError(conf, info, errInvalidExceptionSystem % arg)
|
||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
|
||||
@@ -299,7 +265,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
of "hints": result = contains(conf.options, optHints)
|
||||
of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
|
||||
of "stacktrace": result = contains(conf.options, optStackTrace)
|
||||
of "stacktracemsgs": result = contains(conf.options, optStackTraceMsgs)
|
||||
of "linetrace": result = contains(conf.options, optLineTrace)
|
||||
of "debugger": result = contains(conf.globalOptions, optCDebug)
|
||||
of "profiler": result = contains(conf.options, optProfiler)
|
||||
@@ -309,15 +274,13 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
|
||||
of "infchecks": result = contains(conf.options, optInfCheck)
|
||||
of "nanchecks": result = contains(conf.options, optNaNCheck)
|
||||
of "nilchecks": result = contains(conf.options, optNilCheck)
|
||||
of "objchecks": result = contains(conf.options, optObjCheck)
|
||||
of "fieldchecks": result = contains(conf.options, optFieldCheck)
|
||||
of "rangechecks": result = contains(conf.options, optRangeCheck)
|
||||
of "boundchecks": result = contains(conf.options, optBoundsCheck)
|
||||
of "refchecks":
|
||||
warningDeprecated(conf, info, "refchecks is deprecated!")
|
||||
result = contains(conf.options, optRefCheck)
|
||||
of "refchecks": result = contains(conf.options, optRefCheck)
|
||||
of "overflowchecks": result = contains(conf.options, optOverflowCheck)
|
||||
of "staticboundchecks": result = contains(conf.options, optStaticBoundsCheck)
|
||||
of "stylechecks": result = contains(conf.options, optStyleCheck)
|
||||
of "linedir": result = contains(conf.options, optLineDir)
|
||||
of "assertions", "a": result = contains(conf.options, optAssert)
|
||||
@@ -325,13 +288,13 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
of "symbolfiles": result = conf.symbolFiles != disabledSf
|
||||
of "genscript": result = contains(conf.globalOptions, optGenScript)
|
||||
of "threads": result = contains(conf.globalOptions, optThreads)
|
||||
of "taintmode": result = contains(conf.globalOptions, optTaintMode)
|
||||
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
||||
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
|
||||
of "patterns", "trmacros":
|
||||
if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
|
||||
result = contains(conf.options, optTrMacros)
|
||||
of "patterns", "trmacros": result = contains(conf.options, optTrMacros)
|
||||
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
|
||||
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
||||
of "oldast": result = contains(conf.options, optOldAst)
|
||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
||||
@@ -365,158 +328,56 @@ proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir
|
||||
const
|
||||
errInvalidNumber = "$1 is not a valid number"
|
||||
|
||||
proc makeAbsolute(s: string): AbsoluteFile =
|
||||
if isAbsolute(s):
|
||||
AbsoluteFile pathnorm.normalizePath(s)
|
||||
else:
|
||||
AbsoluteFile pathnorm.normalizePath(os.getCurrentDir() / s)
|
||||
|
||||
proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
|
||||
info: TLineInfo) =
|
||||
## set tracking info, common code for track, trackDirty, & ideTrack
|
||||
var ln, col: int
|
||||
if parseUtils.parseInt(line, ln) <= 0:
|
||||
localError(conf, info, errInvalidNumber % line)
|
||||
if parseUtils.parseInt(column, col) <= 0:
|
||||
localError(conf, info, errInvalidNumber % column)
|
||||
|
||||
let a = makeAbsolute(file)
|
||||
if dirty == "":
|
||||
conf.m.trackPos = newLineInfo(conf, a, ln, col)
|
||||
else:
|
||||
let dirtyOriginalIdx = fileInfoIdx(conf, a)
|
||||
if dirtyOriginalIdx.int32 >= 0:
|
||||
msgs.setDirtyFile(conf, dirtyOriginalIdx, makeAbsolute(dirty))
|
||||
conf.m.trackPos = newLineInfo(dirtyOriginalIdx, ln, col)
|
||||
|
||||
proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
|
||||
var a = arg.split(',')
|
||||
if a.len != 4: localError(conf, info,
|
||||
"DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected")
|
||||
setTrackingInfo(conf, a[0], a[1], a[2], a[3], info)
|
||||
var line, column: int
|
||||
if parseUtils.parseInt(a[2], line) <= 0:
|
||||
localError(conf, info, errInvalidNumber % a[1])
|
||||
if parseUtils.parseInt(a[3], column) <= 0:
|
||||
localError(conf, info, errInvalidNumber % a[2])
|
||||
|
||||
let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1])
|
||||
if dirtyOriginalIdx.int32 >= 0:
|
||||
msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0])
|
||||
|
||||
conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
|
||||
|
||||
proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
|
||||
var a = arg.split(',')
|
||||
if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected")
|
||||
setTrackingInfo(conf, "", a[0], a[1], a[2], info)
|
||||
|
||||
proc trackIde(conf: ConfigRef; cmd: IdeCmd, arg: string, info: TLineInfo) =
|
||||
## set the tracking info related to an ide cmd, supports optional dirty file
|
||||
var a = arg.split(',')
|
||||
case a.len
|
||||
of 4:
|
||||
setTrackingInfo(conf, a[0], a[1], a[2], a[3], info)
|
||||
of 3:
|
||||
setTrackingInfo(conf, "", a[0], a[1], a[2], info)
|
||||
else:
|
||||
localError(conf, info, "[DIRTY_BUFFER,]ORIGINAL_FILE,LINE,COLUMN expected")
|
||||
conf.ideCmd = cmd
|
||||
var line, column: int
|
||||
if parseUtils.parseInt(a[1], line) <= 0:
|
||||
localError(conf, info, errInvalidNumber % a[1])
|
||||
if parseUtils.parseInt(a[2], column) <= 0:
|
||||
localError(conf, info, errInvalidNumber % a[2])
|
||||
conf.m.trackPos = newLineInfo(conf, AbsoluteFile a[0], line, column)
|
||||
|
||||
proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
if pass in {passCmd2, passPP}:
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
options.inclDynlibOverride(conf, arg)
|
||||
|
||||
template handleStdinOrCmdInput =
|
||||
conf.projectFull = conf.projectName.AbsoluteFile
|
||||
conf.projectPath = AbsoluteDir getCurrentDir()
|
||||
if conf.outDir.isEmpty:
|
||||
conf.outDir = getNimcacheDir(conf)
|
||||
|
||||
proc handleStdinInput*(conf: ConfigRef) =
|
||||
conf.projectName = "stdinfile"
|
||||
conf.projectFull = conf.projectName.AbsoluteFile
|
||||
conf.projectPath = AbsoluteDir getCurrentDir()
|
||||
conf.projectIsStdin = true
|
||||
handleStdinOrCmdInput()
|
||||
|
||||
proc handleCmdInput*(conf: ConfigRef) =
|
||||
conf.projectName = "cmdfile"
|
||||
handleStdinOrCmdInput()
|
||||
|
||||
proc parseCommand*(command: string): Command =
|
||||
case command.normalize
|
||||
of "c", "cc", "compile", "compiletoc": cmdCompileToC
|
||||
of "cpp", "compiletocpp": cmdCompileToCpp
|
||||
of "objc", "compiletooc": cmdCompileToOC
|
||||
of "js", "compiletojs": cmdCompileToJS
|
||||
of "r": cmdCrun
|
||||
of "run": cmdTcc
|
||||
of "check": cmdCheck
|
||||
of "e": cmdNimscript
|
||||
of "doc0": cmdDoc0
|
||||
of "doc2", "doc": cmdDoc
|
||||
of "doc2tex": cmdDoc2tex
|
||||
of "rst2html": cmdRst2html
|
||||
of "rst2tex": cmdRst2tex
|
||||
of "jsondoc0": cmdJsondoc0
|
||||
of "jsondoc2", "jsondoc": cmdJsondoc
|
||||
of "ctags": cmdCtags
|
||||
of "buildindex": cmdBuildindex
|
||||
of "gendepend": cmdGendepend
|
||||
of "dump": cmdDump
|
||||
of "parse": cmdParse
|
||||
of "rod": cmdRod
|
||||
of "secret": cmdInteractive
|
||||
of "nop", "help": cmdNop
|
||||
of "jsonscript": cmdJsonscript
|
||||
else: cmdUnknown
|
||||
|
||||
proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
## sets cmd, backend so subsequent flags can query it (e.g. so --gc:arc can be ignored for backendJs)
|
||||
# Note that `--backend` can override the backend, so the logic here must remain reversible.
|
||||
conf.cmd = cmd
|
||||
case cmd
|
||||
of cmdCompileToC, cmdCrun, cmdTcc: conf.backend = backendC
|
||||
of cmdCompileToCpp: conf.backend = backendCpp
|
||||
of cmdCompileToOC: conf.backend = backendObjc
|
||||
of cmdCompileToJS: conf.backend = backendJs
|
||||
else: discard
|
||||
|
||||
proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||
conf.command = command
|
||||
setCmd(conf, command.parseCommand)
|
||||
# command early customizations
|
||||
# must be handled here to honor subsequent `--hint:x:on|off`
|
||||
case conf.cmd
|
||||
of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc.
|
||||
conf.foreignPackageNotes = {hintSuccessX}
|
||||
else:
|
||||
conf.foreignPackageNotes = foreignPackageNotesDefault
|
||||
|
||||
proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) =
|
||||
# Keep this syncronized with the default config/nim.cfg!
|
||||
if cmpIgnoreStyle(key, "nimQuirky") == 0:
|
||||
conf.exc = excQuirky
|
||||
elif cmpIgnoreStyle(key, "release") == 0 or cmpIgnoreStyle(key, "danger") == 0:
|
||||
if pass in {passCmd1, passPP}:
|
||||
conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize}
|
||||
conf.globalOptions.excl {optExcessiveStackTrace, optCDebug}
|
||||
conf.options.incl optOptimizeSpeed
|
||||
if cmpIgnoreStyle(key, "danger") == 0 or cmpIgnoreStyle(key, "quick") == 0:
|
||||
if pass in {passCmd1, passPP}:
|
||||
conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
|
||||
optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir}
|
||||
conf.globalOptions.excl {optCDebug}
|
||||
if conf.outDir.isEmpty:
|
||||
conf.outDir = getNimcacheDir(conf)
|
||||
|
||||
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf: ConfigRef) =
|
||||
var
|
||||
key, val: string
|
||||
case switch.normalize
|
||||
of "eval":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.projectIsCmd = true
|
||||
conf.cmdInput = arg # can be empty (a nim file with empty content is valid too)
|
||||
if conf.cmd == cmdNone:
|
||||
conf.command = "e"
|
||||
conf.setCmd cmdNimscript # better than `cmdCrun` as a default
|
||||
conf.implicitCmd = true
|
||||
of "path", "p":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
for path in nimbleSubs(conf, arg):
|
||||
addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
|
||||
else: processPath(conf, path, info), info)
|
||||
addPath(conf, if pass == passPP: processCfgPath(conf, arg, info)
|
||||
else: processPath(conf, arg, info), info)
|
||||
of "nimblepath", "babelpath":
|
||||
if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath")
|
||||
# keep the old name for compat
|
||||
if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var path = processPath(conf, arg, info, notRelativeToProj=true)
|
||||
@@ -525,7 +386,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
path = nimbleDir / RelativeDir"pkgs"
|
||||
nimblePath(conf, path, info)
|
||||
of "nonimblepath", "nobabelpath":
|
||||
if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath")
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
disableNimblePath(conf)
|
||||
of "clearnimblepath":
|
||||
@@ -538,10 +398,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf.lazyPaths.keepItIf(it != path)
|
||||
of "nimcache":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var arg = arg
|
||||
# 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, arg, info, notRelativeToProj=true)
|
||||
of "out", "o":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
@@ -551,30 +407,24 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "outdir":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.outDir = processPath(conf, arg, info, notRelativeToProj=true)
|
||||
of "usenimcache":
|
||||
processOnOffSwitchG(conf, {optUseNimcache}, arg, pass, info)
|
||||
of "docseesrcurl":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.docSeeSrcUrl = arg
|
||||
of "docroot":
|
||||
conf.docRoot = if arg.len == 0: docRootDefault else: arg
|
||||
of "backend", "b":
|
||||
let backend = parseEnum(arg.normalize, TBackend.default)
|
||||
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
|
||||
conf.backend = backend
|
||||
of "doccmd": conf.docCmd = arg
|
||||
of "mainmodule", "m":
|
||||
discard "allow for backwards compatibility, but don't do anything"
|
||||
of "define", "d":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if {':', '='} in arg:
|
||||
splitSwitch(conf, arg, key, val, pass, info)
|
||||
specialDefine(conf, key, pass)
|
||||
defineSymbol(conf.symbols, key, val)
|
||||
else:
|
||||
specialDefine(conf, arg, pass)
|
||||
defineSymbol(conf.symbols, arg)
|
||||
of "undef", "u":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
undefSymbol(conf.symbols, arg)
|
||||
of "symbol":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# deprecated, do nothing
|
||||
of "compile":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}: processCompile(conf, arg)
|
||||
@@ -595,50 +445,24 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "forcebuild", "f":
|
||||
processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
|
||||
of "project":
|
||||
processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
|
||||
of "gc", "mm":
|
||||
if conf.backend == backendJs: return # for: bug #16033
|
||||
processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
|
||||
of "gc":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
case arg.normalize
|
||||
of "boehm":
|
||||
conf.selectedGC = gcBoehm
|
||||
defineSymbol(conf.symbols, "boehmgc")
|
||||
incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
|
||||
of "refc":
|
||||
conf.selectedGC = gcRefc
|
||||
of "v2":
|
||||
message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
|
||||
of "markandsweep":
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
of "destructors", "arc":
|
||||
conf.selectedGC = gcArc
|
||||
of "destructors":
|
||||
conf.selectedGC = gcDestructors
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
defineSymbol(conf.symbols, "gcarc")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
of "orc":
|
||||
conf.selectedGC = gcOrc
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
of "hooks":
|
||||
conf.selectedGC = gcHooks
|
||||
defineSymbol(conf.symbols, "gchooks")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "go":
|
||||
conf.selectedGC = gcGo
|
||||
defineSymbol(conf.symbols, "gogc")
|
||||
@@ -646,23 +470,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf.selectedGC = gcNone
|
||||
defineSymbol(conf.symbols, "nogc")
|
||||
of "stack", "regions":
|
||||
conf.selectedGC = gcRegions
|
||||
conf.selectedGC= gcRegions
|
||||
defineSymbol(conf.symbols, "gcregions")
|
||||
of "v2": warningOptionNoop(arg)
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "warnings", "w":
|
||||
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
||||
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
||||
of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf)
|
||||
of "warningaserror": processSpecificNote(arg, wWarningAsError, pass, info, switch, conf)
|
||||
of "hintaserror": processSpecificNote(arg, wHintAsError, pass, info, switch, conf)
|
||||
of "hints":
|
||||
if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf)
|
||||
of "threadanalysis":
|
||||
if conf.backend == backendJs: discard
|
||||
else: processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
|
||||
of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
|
||||
of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
|
||||
of "stacktracemsgs": processOnOffSwitch(conf, {optStackTraceMsgs}, arg, pass, info)
|
||||
of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
|
||||
of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
|
||||
of "debugger":
|
||||
@@ -699,32 +517,43 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
else:
|
||||
undefSymbol(conf.symbols, "hotcodereloading")
|
||||
undefSymbol(conf.symbols, "useNimRtl")
|
||||
of "oldnewlines":
|
||||
case arg.normalize
|
||||
of "","on":
|
||||
conf.oldNewlines = true
|
||||
defineSymbol(conf.symbols, "nimOldNewlines")
|
||||
of "off":
|
||||
conf.oldNewlines = false
|
||||
undefSymbol(conf.symbols, "nimOldNewlines")
|
||||
else:
|
||||
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
|
||||
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
|
||||
of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info)
|
||||
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
|
||||
of "floatchecks":
|
||||
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
|
||||
of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info)
|
||||
of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info)
|
||||
of "nilchecks": processOnOffSwitch(conf, {optNilCheck}, arg, pass, info)
|
||||
of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info)
|
||||
of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
|
||||
of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
|
||||
of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
|
||||
of "refchecks":
|
||||
warningDeprecated(conf, info, "refchecks is deprecated!")
|
||||
processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
|
||||
of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
|
||||
of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
|
||||
of "staticboundchecks": processOnOffSwitch(conf, {optStaticBoundsCheck}, arg, pass, info)
|
||||
of "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info)
|
||||
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
|
||||
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
|
||||
of "deadcodeelim": discard # deprecated, dead code elim always on
|
||||
of "threads":
|
||||
if conf.backend == backendJs: discard
|
||||
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
||||
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
|
||||
processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
||||
#if optThreads in conf.globalOptions: incl(conf.notes, warnGcUnsafe)
|
||||
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
|
||||
of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
|
||||
of "implicitstatic":
|
||||
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
|
||||
of "patterns", "trmacros":
|
||||
if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
|
||||
processOnOffSwitch(conf, {optTrMacros}, arg, pass, info)
|
||||
of "opt":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
@@ -792,8 +621,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
of "listcmd":
|
||||
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||
of "asm":
|
||||
processOnOffSwitchG(conf, {optProduceAsm}, arg, pass, info)
|
||||
of "genmapping":
|
||||
processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
|
||||
of "os":
|
||||
@@ -814,9 +641,6 @@ 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)
|
||||
of "maxloopiterationsvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.maxLoopIterationsVM = parseInt(arg)
|
||||
of "errormax":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# Note: `nim check` (etc) can overwrite this.
|
||||
@@ -831,10 +655,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if verbosity notin {0..3}:
|
||||
localError(conf, info, "invalid verbosity level: '$1'" % arg)
|
||||
conf.verbosity = verbosity
|
||||
var verb = NotesVerbosity[conf.verbosity]
|
||||
## We override the default `verb` by explicitly modified (set/unset) notes.
|
||||
conf.notes = (conf.modifiedyNotes * conf.notes + verb) -
|
||||
(conf.modifiedyNotes * verb - conf.notes)
|
||||
conf.notes = NotesVerbosity[conf.verbosity]
|
||||
incl(conf.notes, conf.enableNotes)
|
||||
excl(conf.notes, conf.disableNotes)
|
||||
conf.mainPackageNotes = conf.notes
|
||||
of "parallelbuild":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
@@ -851,19 +674,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "help", "h":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
helpOnError(conf, pass)
|
||||
of "symbolfiles", "incremental", "ic":
|
||||
if switch.normalize == "symbolfiles": deprecatedAlias(switch, "incremental")
|
||||
# xxx maybe also ic, since not in help?
|
||||
if pass in {passCmd2, passPP}:
|
||||
case arg.normalize
|
||||
of "on": conf.symbolFiles = v2Sf
|
||||
of "off": conf.symbolFiles = disabledSf
|
||||
of "writeonly": conf.symbolFiles = writeOnlySf
|
||||
of "readonly": conf.symbolFiles = readOnlySf
|
||||
of "v2": conf.symbolFiles = v2Sf
|
||||
of "stress": conf.symbolFiles = stressTest
|
||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||
setUseIc(conf.symbolFiles != disabledSf)
|
||||
of "symbolfiles": discard "ignore for backwards compat"
|
||||
of "incremental":
|
||||
when not nimIncremental:
|
||||
localError(conf, info, "the compiler was not built with " &
|
||||
"incremental compilation features; bootstrap with " &
|
||||
"-d:nimIncremental to enable")
|
||||
case arg.normalize
|
||||
of "on": conf.symbolFiles = v2Sf
|
||||
of "off": conf.symbolFiles = disabledSf
|
||||
of "writeonly": conf.symbolFiles = writeOnlySf
|
||||
of "readonly": conf.symbolFiles = readOnlySf
|
||||
of "v2": conf.symbolFiles = v2Sf
|
||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||
of "skipcfg":
|
||||
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
|
||||
of "skipprojcfg":
|
||||
@@ -873,7 +696,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "skipparentcfg":
|
||||
processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
|
||||
of "genscript", "gendeps":
|
||||
if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
|
||||
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
||||
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
||||
@@ -885,9 +707,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
splitSwitch(conf, arg, key, val, pass, info)
|
||||
os.putEnv(key, val)
|
||||
of "cc":
|
||||
if conf.backend != backendJs: # bug #19330
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
setCC(conf, arg, info)
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
setCC(conf, arg, info)
|
||||
of "track":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
track(conf, arg, info)
|
||||
@@ -898,50 +719,28 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
conf.ideCmd = ideSug
|
||||
of "def":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
conf.ideCmd = ideDef
|
||||
of "eval":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
trackIde(conf, ideDef, arg, info)
|
||||
conf.evalExpr = arg
|
||||
of "context":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
conf.ideCmd = ideCon
|
||||
of "usages":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
trackIde(conf, ideUse, arg, info)
|
||||
of "defusages":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
trackIde(conf, ideDus, arg, info)
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
conf.ideCmd = ideUse
|
||||
of "stdout":
|
||||
processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
|
||||
of "filenames":
|
||||
case arg.normalize
|
||||
of "abs": conf.filenameOption = foAbs
|
||||
of "canonical": conf.filenameOption = foCanonical
|
||||
of "legacyrelproj": conf.filenameOption = foLegacyRelProj
|
||||
else: localError(conf, info, "expected: abs|canonical|legacyRelProj, got: $1" % arg)
|
||||
of "processing":
|
||||
incl(conf.notes, hintProcessing)
|
||||
incl(conf.mainPackageNotes, hintProcessing)
|
||||
case arg.normalize
|
||||
of "dots": conf.hintProcessingDots = true
|
||||
of "filenames": conf.hintProcessingDots = false
|
||||
of "off":
|
||||
excl(conf.notes, hintProcessing)
|
||||
excl(conf.mainPackageNotes, hintProcessing)
|
||||
else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg)
|
||||
of "unitsep":
|
||||
conf.unitSep = if switchOn(arg): "\31" else: ""
|
||||
of "listfullpaths":
|
||||
# xxx in future work, use `warningDeprecated`
|
||||
conf.filenameOption = if switchOn(arg): foAbs else: foCanonical
|
||||
of "spellsuggest":
|
||||
if arg.len == 0: conf.spellSuggestMax = spellSuggestSecretSauce
|
||||
elif arg == "auto": conf.spellSuggestMax = spellSuggestSecretSauce
|
||||
else: conf.spellSuggestMax = parseInt(arg)
|
||||
of "declaredlocs":
|
||||
processOnOffSwitchG(conf, {optDeclaredLocs}, arg, pass, info)
|
||||
processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
|
||||
of "dynliboverride":
|
||||
dynlibOverride(conf, switch, arg, pass, info)
|
||||
of "dynliboverrideall":
|
||||
processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
|
||||
of "cs":
|
||||
# only supported for compatibility. Does nothing.
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
of "experimental":
|
||||
if arg.len == 0:
|
||||
conf.features.incl oldExperimentalFeatures
|
||||
@@ -957,21 +756,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
localError(conf, info, "unknown obsolete feature")
|
||||
of "nocppexceptions":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
conf.exc = low(ExceptionSystem)
|
||||
incl(conf.globalOptions, optNoCppExceptions)
|
||||
defineSymbol(conf.symbols, "noCppExceptions")
|
||||
of "exceptions":
|
||||
case arg.normalize
|
||||
of "cpp": conf.exc = excCpp
|
||||
of "setjmp": conf.exc = excSetjmp
|
||||
of "quirky": conf.exc = excQuirky
|
||||
of "goto": conf.exc = excGoto
|
||||
else: localError(conf, info, errInvalidExceptionSystem % arg)
|
||||
of "cppdefine":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if conf != nil:
|
||||
conf.cppDefine(arg)
|
||||
of "newruntime":
|
||||
warningDeprecated(conf, info, "newruntime is deprecated, use arc/orc instead!")
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
doAssert(conf != nil)
|
||||
@@ -980,20 +771,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
incl(conf.globalOptions, optOwnedRefs)
|
||||
incl(conf.globalOptions, optSeqDestructors)
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
conf.selectedGC = gcHooks
|
||||
defineSymbol(conf.symbols, "gchooks")
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimOwnedEnabled")
|
||||
of "seqsv2":
|
||||
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
conf.selectedGC = gcDestructors
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
of "stylecheck":
|
||||
case arg.normalize
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint}
|
||||
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
|
||||
of "usages": conf.globalOptions.incl optStyleUsages
|
||||
else: localError(conf, info, errOffHintsError % arg)
|
||||
of "showallmismatches":
|
||||
processOnOffSwitchG(conf, {optShowAllMismatches}, arg, pass, info)
|
||||
@@ -1010,59 +794,29 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "expandmacro":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.macrosToExpand[arg] = "T"
|
||||
of "expandarc":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.arcToExpand[arg] = "T"
|
||||
of "oldgensym":
|
||||
processOnOffSwitchG(conf, {optNimV019}, arg, pass, info)
|
||||
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
|
||||
discard "the default"
|
||||
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":
|
||||
processOnOffSwitchG(conf, {optProfileVM}, arg, pass, info)
|
||||
of "sinkinference":
|
||||
processOnOffSwitch(conf, {optSinkInference}, arg, pass, info)
|
||||
of "cursorinference":
|
||||
# undocumented, for debugging purposes only:
|
||||
processOnOffSwitch(conf, {optCursorInference}, arg, pass, info)
|
||||
of "panics":
|
||||
processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
|
||||
if optPanics in conf.globalOptions:
|
||||
defineSymbol(conf.symbols, "nimPanics")
|
||||
of "sourcemap": # xxx document in --fullhelp
|
||||
conf.globalOptions.incl optSourcemap
|
||||
conf.options.incl optLineDir
|
||||
of "deepcopy":
|
||||
processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
|
||||
localError(conf, info, "unknown Nim version; currently supported values are: {1.0}")
|
||||
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
||||
handleStdinInput(conf)
|
||||
of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
|
||||
of "nimmainprefix": conf.nimMainPrefix = arg
|
||||
else:
|
||||
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
|
||||
else: invalidCmdLineOption(conf, pass, switch, info)
|
||||
|
||||
template gCmdLineInfo*(): untyped = newLineInfo(commandLineIdx, 1, 1)
|
||||
|
||||
proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
|
||||
var cmd, arg: string
|
||||
splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
|
||||
processSwitch(cmd, arg, pass, gCmdLineInfo, config)
|
||||
|
||||
|
||||
proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
|
||||
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
|
||||
# we transform it to (key = hint, val = [X]:off)
|
||||
@@ -1076,22 +830,19 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
|
||||
|
||||
proc processArgument*(pass: TCmdLinePass; p: OptParser;
|
||||
argsCount: var int; config: ConfigRef): bool =
|
||||
if argsCount == 0 and config.implicitCmd:
|
||||
argsCount.inc
|
||||
if argsCount == 0:
|
||||
# nim filename.nims is the same as "nim e filename.nims":
|
||||
if p.key.endsWith(".nims"):
|
||||
config.setCmd cmdNimscript
|
||||
config.command = "e"
|
||||
incl(config.globalOptions, optWasNimscript)
|
||||
config.projectName = unixToNativePath(p.key)
|
||||
config.arguments = cmdLineRest(p)
|
||||
result = true
|
||||
elif pass != passCmd2: setCommandEarly(config, p.key)
|
||||
elif pass != passCmd2:
|
||||
config.command = p.key
|
||||
else:
|
||||
if pass == passCmd1: config.commandArgs.add p.key
|
||||
if argsCount == 1:
|
||||
if p.key.endsWith(".nims"):
|
||||
incl(config.globalOptions, optWasNimscript)
|
||||
# support UNIX style filenames everywhere for portable build scripts:
|
||||
if config.projectName.len == 0:
|
||||
config.projectName = unixToNativePath(p.key)
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets
|
||||
|
||||
from magicsys import addSonSkipIntLit
|
||||
|
||||
const
|
||||
logBindings = false
|
||||
|
||||
## Code dealing with Concept declarations
|
||||
## --------------------------------------
|
||||
|
||||
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"), 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
|
||||
## containing 'proc' declarations and the like.
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = semConceptDecl(c, n[i])
|
||||
of nkProcDef..nkIteratorDef, nkFuncDef:
|
||||
result = c.semExpr(c, n, {efWantStmt})
|
||||
of nkTypeClassTy:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len-1:
|
||||
result[i] = n[i]
|
||||
result[^1] = semConceptDecl(c, n[^1])
|
||||
else:
|
||||
localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
|
||||
result = n
|
||||
|
||||
proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
||||
## Semantic checking for the concept declaration. Runs
|
||||
## when we process the concept itself, not its matching process.
|
||||
assert n.kind == nkTypeClassTy
|
||||
inc c.inConceptDecl
|
||||
openScope(c)
|
||||
declareSelf(c, n.info)
|
||||
result = semConceptDecl(c, n)
|
||||
rawCloseScope(c)
|
||||
dec c.inConceptDecl
|
||||
|
||||
## Concept matching
|
||||
## ----------------
|
||||
|
||||
type
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
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.
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
## concrete type.
|
||||
for i in 0..<m.inferred.len:
|
||||
if m.inferred[i][0] == key: return m.inferred[i][1]
|
||||
return nil
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; 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.
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.lastSon, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
#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 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
|
||||
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.sons[0], a.sons[0], m)
|
||||
elif m.magic == mArrPut:
|
||||
result = matchType(c, f.sons[0], a, m)
|
||||
else:
|
||||
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.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 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.inferred.setLen oldLenB
|
||||
|
||||
result = covered >= a.len
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
else:
|
||||
for i in 0..<f.len:
|
||||
result = matchType(c, f[i], a, m)
|
||||
if result: break # and remember the binding!
|
||||
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
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## Like 'matchType' but with extra logic dealing with proc return types
|
||||
## which can be nil or the 'void' type.
|
||||
if f.isEmptyType:
|
||||
result = a.isEmptyType
|
||||
elif a == nil:
|
||||
result = false
|
||||
else:
|
||||
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.
|
||||
let oldLen = m.inferred.len
|
||||
|
||||
let can = candidate.typ.n
|
||||
let con = n[0].sym.typ.n
|
||||
|
||||
if can.len < con.len:
|
||||
# too few arguments, cannot be a match:
|
||||
return false
|
||||
|
||||
let common = min(can.len, con.len)
|
||||
for i in 1 ..< common:
|
||||
if not matchType(c, con[i].typ, can[i].typ, m):
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
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:
|
||||
for i in common ..< can.len:
|
||||
assert can[i].kind == nkSym
|
||||
if can[i].sym.ast == nil:
|
||||
# has too many arguments one of which is not optional:
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
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.
|
||||
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): 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'
|
||||
## can be matched with the current scope.
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for i in 0..<n.len:
|
||||
if not conceptMatchNode(c, n[i], m):
|
||||
return false
|
||||
return true
|
||||
of nkProcDef, nkFuncDef:
|
||||
# procs match any of: proc, template, macro, func, method, converter.
|
||||
# The others are more specific.
|
||||
# XXX: Enforce .noSideEffect for 'nkFuncDef'? But then what are the use cases...
|
||||
const filter = {skProc, skTemplate, skMacro, skFunc, skMethod, skConverter}
|
||||
result = matchSyms(c, n, filter, m)
|
||||
of nkTemplateDef:
|
||||
result = matchSyms(c, n, {skTemplate}, m)
|
||||
of nkMacroDef:
|
||||
result = matchSyms(c, n, {skMacro}, m)
|
||||
of nkConverterDef:
|
||||
result = matchSyms(c, n, {skConverter}, m)
|
||||
of nkMethodDef:
|
||||
result = matchSyms(c, n, {skMethod}, m)
|
||||
of nkIteratorDef:
|
||||
result = matchSyms(c, n, {skIterator}, m)
|
||||
else:
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
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 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(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])
|
||||
@@ -13,7 +13,7 @@ import
|
||||
strtabs
|
||||
|
||||
from options import Feature
|
||||
from lineinfos import hintMin, hintMax, warnMin, warnMax
|
||||
from lineinfos import HintsToStr, WarningsToStr
|
||||
|
||||
proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") =
|
||||
symbols[symbol] = value
|
||||
@@ -34,110 +34,69 @@ proc countDefinedSymbols*(symbols: StringTableRef): int =
|
||||
proc initDefines*(symbols: StringTableRef) =
|
||||
# for bootstrapping purposes and old code:
|
||||
template defineSymbol(s) = symbols.defineSymbol(s)
|
||||
defineSymbol("nimhygiene") # deadcode
|
||||
defineSymbol("niminheritable") # deadcode
|
||||
defineSymbol("nimmixin") # deadcode
|
||||
defineSymbol("nimeffects") # deadcode
|
||||
defineSymbol("nimbabel") # deadcode
|
||||
defineSymbol("nimcomputedgoto") # deadcode
|
||||
defineSymbol("nimunion") # deadcode
|
||||
defineSymbol("nimnewshared") # deadcode
|
||||
defineSymbol("nimNewTypedesc") # deadcode
|
||||
defineSymbol("nimrequiresnimframe") # deadcode
|
||||
defineSymbol("nimparsebiggestfloatmagic") # deadcode
|
||||
defineSymbol("nimalias") # deadcode
|
||||
defineSymbol("nimlocks") # 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
|
||||
defineSymbol("nimArrIdx") # deadcode
|
||||
defineSymbol("nimHasalignOf") # deadcode
|
||||
defineSymbol("nimDistros") # deadcode
|
||||
defineSymbol("nimHasCppDefine") # deadcode
|
||||
defineSymbol("nimGenericInOutFlags") # deadcode
|
||||
when false: defineSymbol("nimHasOpt") # deadcode
|
||||
defineSymbol("nimNoArrayToCstringConversion") # deadcode
|
||||
defineSymbol("nimHasRunnableExamples") # deadcode
|
||||
defineSymbol("nimNewDot") # deadcode
|
||||
defineSymbol("nimHasNilChecks") # deadcode
|
||||
defineSymbol("nimSymKind") # deadcode
|
||||
defineSymbol("nimVmEqIdent") # deadcode
|
||||
defineSymbol("nimNoNil") # deadcode
|
||||
defineSymbol("nimNoZeroTerminator") # deadcode
|
||||
defineSymbol("nimNotNil") # deadcode
|
||||
defineSymbol("nimVmExportFixed") # deadcode
|
||||
defineSymbol("nimHasSymOwnerInMacro") # deadcode
|
||||
defineSymbol("nimNewRuntime") # deadcode
|
||||
defineSymbol("nimIncrSeqV3") # xxx: turn this into deadcode
|
||||
defineSymbol("nimAshr") # deadcode
|
||||
defineSymbol("nimNoNilSeqs") # deadcode
|
||||
defineSymbol("nimNoNilSeqs2") # deadcode
|
||||
defineSymbol("nimHasUserErrors") # deadcode
|
||||
defineSymbol("nimUncheckedArrayTyp") # deadcode
|
||||
defineSymbol("nimHasTypeof") # deadcode
|
||||
defineSymbol("nimErrorProcCanHaveBody") # deadcode
|
||||
defineSymbol("nimHasInstantiationOfInMacro") # deadcode
|
||||
defineSymbol("nimHasHotCodeReloading") # deadcode
|
||||
defineSymbol("nimHasNilSeqs") # deadcode
|
||||
defineSymbol("nimHasSignatureHashInMacro") # deadcode
|
||||
defineSymbol("nimHasDefault") # deadcode
|
||||
defineSymbol("nimMacrosSizealignof") # deadcode
|
||||
|
||||
# > 0.20.0
|
||||
defineSymbol("nimhygiene")
|
||||
defineSymbol("niminheritable")
|
||||
defineSymbol("nimmixin")
|
||||
defineSymbol("nimeffects")
|
||||
defineSymbol("nimbabel")
|
||||
defineSymbol("nimcomputedgoto")
|
||||
defineSymbol("nimunion")
|
||||
defineSymbol("nimnewshared")
|
||||
defineSymbol("nimNewTypedesc")
|
||||
defineSymbol("nimrequiresnimframe")
|
||||
defineSymbol("nimparsebiggestfloatmagic")
|
||||
defineSymbol("nimalias")
|
||||
defineSymbol("nimlocks")
|
||||
defineSymbol("nimnode")
|
||||
defineSymbol("nimnomagic64")
|
||||
defineSymbol("nimvarargstyped")
|
||||
defineSymbol("nimtypedescfixed")
|
||||
defineSymbol("nimKnowsNimvm")
|
||||
defineSymbol("nimArrIdx")
|
||||
defineSymbol("nimHasalignOf")
|
||||
defineSymbol("nimNewShiftOps")
|
||||
defineSymbol("nimDistros")
|
||||
defineSymbol("nimHasCppDefine")
|
||||
defineSymbol("nimGenericInOutFlags")
|
||||
when false: defineSymbol("nimHasOpt")
|
||||
defineSymbol("nimNoArrayToCstringConversion")
|
||||
defineSymbol("nimNewRoof")
|
||||
defineSymbol("nimHasRunnableExamples")
|
||||
defineSymbol("nimNewDot")
|
||||
defineSymbol("nimHasNilChecks")
|
||||
defineSymbol("nimSymKind")
|
||||
defineSymbol("nimVmEqIdent")
|
||||
defineSymbol("nimNoNil")
|
||||
defineSymbol("nimNoZeroTerminator")
|
||||
defineSymbol("nimNotNil")
|
||||
defineSymbol("nimVmExportFixed")
|
||||
defineSymbol("nimHasSymOwnerInMacro")
|
||||
defineSymbol("nimNewRuntime")
|
||||
defineSymbol("nimIncrSeqV3")
|
||||
defineSymbol("nimAshr")
|
||||
defineSymbol("nimNoNilSeqs")
|
||||
defineSymbol("nimNoNilSeqs2")
|
||||
defineSymbol("nimHasUserErrors")
|
||||
defineSymbol("nimUncheckedArrayTyp")
|
||||
defineSymbol("nimHasTypeof")
|
||||
defineSymbol("nimErrorProcCanHaveBody")
|
||||
defineSymbol("nimHasInstantiationOfInMacro")
|
||||
defineSymbol("nimHasHotCodeReloading")
|
||||
defineSymbol("nimHasNilSeqs")
|
||||
defineSymbol("nimHasSignatureHashInMacro")
|
||||
defineSymbol("nimHasDefault")
|
||||
defineSymbol("nimMacrosSizealignof")
|
||||
defineSymbol("nimNoZeroExtendMagic")
|
||||
defineSymbol("nimMacrosGetNodeId")
|
||||
for f in Feature:
|
||||
for f in low(Feature)..high(Feature):
|
||||
defineSymbol("nimHas" & $f)
|
||||
|
||||
for s in warnMin..warnMax:
|
||||
defineSymbol("nimHasWarning" & $s)
|
||||
for s in hintMin..hintMax:
|
||||
defineSymbol("nimHasHint" & $s)
|
||||
for s in WarningsToStr:
|
||||
defineSymbol("nimHasWarning" & s)
|
||||
for s in HintsToStr:
|
||||
defineSymbol("nimHasHint" & s)
|
||||
|
||||
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")
|
||||
|
||||
defineSymbol("nimHasSinkInference")
|
||||
defineSymbol("nimNewIntegerOps")
|
||||
defineSymbol("nimHasInvariant")
|
||||
defineSymbol("nimHasStacktraceMsgs")
|
||||
defineSymbol("nimDoesntTrackDefects")
|
||||
defineSymbol("nimHasLentIterators")
|
||||
defineSymbol("nimHasDeclaredMagic")
|
||||
defineSymbol("nimHasStacktracesModule")
|
||||
defineSymbol("nimHasEffectTraitsModule")
|
||||
defineSymbol("nimHasCastPragmaBlocks")
|
||||
defineSymbol("nimHasDeclaredLocs")
|
||||
defineSymbol("nimHasJsBigIntBackend")
|
||||
defineSymbol("nimHasWarningAsError")
|
||||
defineSymbol("nimHasHintAsError")
|
||||
defineSymbol("nimHasSpellSuggest")
|
||||
defineSymbol("nimHasCustomLiterals")
|
||||
defineSymbol("nimHasUnifiedTuple")
|
||||
defineSymbol("nimHasIterable")
|
||||
defineSymbol("nimHasTypeofVoid")
|
||||
defineSymbol("nimHasDragonBox")
|
||||
defineSymbol("nimHasHintAll")
|
||||
defineSymbol("nimHasTrace")
|
||||
defineSymbol("nimHasEffectsOf")
|
||||
|
||||
defineSymbol("nimHasEnforceNoRaises")
|
||||
defineSymbol("nimHasWarnBareExcept")
|
||||
|
||||
6
compiler/configuration.nim
Normal file
6
compiler/configuration.nim
Normal file
@@ -0,0 +1,6 @@
|
||||
## Use the module 'lineinfos' instead!
|
||||
|
||||
{.deprecated.}
|
||||
|
||||
import lineinfos
|
||||
export lineinfos
|
||||
@@ -1,56 +0,0 @@
|
||||
##[
|
||||
Utilities to help with debugging nim compiler.
|
||||
|
||||
Experimental API, subject to change.
|
||||
]##
|
||||
|
||||
#[
|
||||
## example
|
||||
useful debugging flags:
|
||||
--stacktrace -d:debug -d:nimDebugUtils
|
||||
nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim
|
||||
|
||||
## future work
|
||||
* expose and improve astalgo.debug, replacing it by std/prettyprints,
|
||||
refs https://github.com/nim-lang/RFCs/issues/385
|
||||
]#
|
||||
|
||||
import options
|
||||
import std/wrapnils
|
||||
export wrapnils
|
||||
# allows using things like: `?.n.sym.typ.len`
|
||||
|
||||
import std/stackframes
|
||||
export stackframes
|
||||
# allows using things like: `setFrameMsg c.config$n.info & " " & $n.kind`
|
||||
# which doesn't log, but augments stacktrace with side channel information
|
||||
|
||||
var conf0: ConfigRef
|
||||
|
||||
proc onNewConfigRef*(conf: ConfigRef) {.inline.} =
|
||||
## Caches `conf`, which can be retrieved with `getConfigRef`.
|
||||
## This avoids having to forward `conf` all the way down the call chain to
|
||||
## procs that need it during a debugging session.
|
||||
conf0 = conf
|
||||
|
||||
proc getConfigRef*(): ConfigRef =
|
||||
## nil, if -d:nimDebugUtils wasn't specified
|
||||
result = conf0
|
||||
|
||||
proc isCompilerDebug*(): bool =
|
||||
##[
|
||||
Provides a simple way for user code to enable/disable logging in the compiler
|
||||
in a granular way. This can then be used in the compiler as follows:
|
||||
```nim
|
||||
if isCompilerDebug():
|
||||
echo ?.n.sym.typ.len
|
||||
```
|
||||
]##
|
||||
runnableExamples:
|
||||
proc main =
|
||||
echo 2
|
||||
{.define(nimCompilerDebug).}
|
||||
echo 3.5 # code section in which `isCompilerDebug` will be true
|
||||
{.undef(nimCompilerDebug).}
|
||||
echo 'x'
|
||||
conf0.isDefined("nimCompilerDebug")
|
||||
@@ -9,12 +9,10 @@
|
||||
|
||||
# This module implements a dependency file generator.
|
||||
|
||||
import options, ast, ropes, passes, pathutils, msgs, lineinfos
|
||||
import
|
||||
options, ast, ropes, idents, passes, modulepaths, pathutils
|
||||
|
||||
import modulegraphs
|
||||
|
||||
import std/[os, strutils, parseutils]
|
||||
import std/private/globs
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
type
|
||||
TGen = object of PPassContext
|
||||
@@ -27,65 +25,23 @@ type
|
||||
dotGraph: Rope
|
||||
|
||||
proc addDependencyAux(b: Backend; importing, imported: string) =
|
||||
b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)])
|
||||
addf(b.dotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)])
|
||||
# s1 -> s2_4[label="[0-9]"];
|
||||
|
||||
proc toNimblePath(s: string, isStdlib: bool): string =
|
||||
const stdPrefix = "std/"
|
||||
const pkgPrefix = "pkg/"
|
||||
if isStdlib:
|
||||
let sub = "lib/"
|
||||
var start = s.find(sub)
|
||||
if start < 0:
|
||||
doAssert false
|
||||
else:
|
||||
start += sub.len
|
||||
let base = s[start..^1]
|
||||
|
||||
if base.startsWith("system") or base.startsWith("std"):
|
||||
result = base
|
||||
else:
|
||||
for dir in stdlibDirs:
|
||||
if base.startsWith(dir):
|
||||
return stdPrefix & base.splitFile.name
|
||||
|
||||
result = stdPrefix & base
|
||||
else:
|
||||
var sub = getEnv("NIMBLE_DIR")
|
||||
if sub.len == 0:
|
||||
sub = ".nimble/pkgs/"
|
||||
else:
|
||||
sub.add "/pkgs/"
|
||||
var start = s.find(sub)
|
||||
if start < 0:
|
||||
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
|
||||
|
||||
let path = splitFile(toProjPath(g.config, n.sym.position.FileIndex))
|
||||
let modulePath = splitFile(toProjPath(g.config, g.module.position.FileIndex))
|
||||
let parent = nativeToUnixPath(modulePath.dir / modulePath.name).toNimblePath(belongsToStdlib(g.graph, g.module))
|
||||
let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
|
||||
addDependencyAux(b, parent, child)
|
||||
|
||||
proc addDotDependency(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
let g = PGen(c)
|
||||
let b = Backend(g.graph.backend)
|
||||
case n.kind
|
||||
of nkImportStmt:
|
||||
for i in 0..<n.len:
|
||||
addDependency(c, g, b, n[i])
|
||||
for i in 0 ..< len(n):
|
||||
var imported = getModuleName(g.config, n.sons[i])
|
||||
addDependencyAux(b, g.module.name.s, imported)
|
||||
of nkFromStmt, nkImportExceptStmt:
|
||||
addDependency(c, g, b, n[0])
|
||||
var imported = getModuleName(g.config, n.sons[0])
|
||||
addDependencyAux(b, g.module.name.s, imported)
|
||||
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
|
||||
for i in 0..<n.len: discard addDotDependency(c, n[i])
|
||||
for i in 0 ..< len(n): discard addDotDependency(c, n.sons[i])
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -95,10 +51,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
|
||||
rope(project.splitFile.name), b.dotGraph],
|
||||
changeFileExt(project, "dot"))
|
||||
|
||||
when not defined(nimHasSinkInference):
|
||||
{.pragma: nosinks.}
|
||||
|
||||
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
|
||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
var g: PGen
|
||||
new(g)
|
||||
g.module = module
|
||||
|
||||
564
compiler/dfa.nim
564
compiler/dfa.nim
@@ -29,44 +29,47 @@
|
||||
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
|
||||
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
|
||||
|
||||
import ast, intsets, lineinfos, renderer
|
||||
import std/private/asciitables
|
||||
import ast, types, intsets, lineinfos, renderer
|
||||
|
||||
from patterns import sameTrees
|
||||
|
||||
type
|
||||
InstrKind* = enum
|
||||
goto, fork, def, use
|
||||
goto, fork, join, def, use
|
||||
Instr* = object
|
||||
n*: PNode # contains the def/use location.
|
||||
n*: PNode
|
||||
case kind*: InstrKind
|
||||
of goto, fork: dest*: int
|
||||
else: discard
|
||||
of def, use: sym*: PSym # 'sym' can also be 'nil' and
|
||||
# then 'n' contains the def/use location.
|
||||
# This is used so that we can track object
|
||||
# and tuple field accesses precisely.
|
||||
of goto, fork, join: dest*: int
|
||||
|
||||
ControlFlowGraph* = seq[Instr]
|
||||
|
||||
TPosition = distinct int
|
||||
|
||||
TBlock = object
|
||||
case isTryBlock: bool
|
||||
of false:
|
||||
label: PSym
|
||||
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
|
||||
label: PSym
|
||||
fixups: seq[TPosition]
|
||||
|
||||
Con = object
|
||||
code: ControlFlowGraph
|
||||
inTryStmt: int
|
||||
inCall, inTryStmt: int
|
||||
blocks: seq[TBlock]
|
||||
tryStmtFixups: seq[TPosition]
|
||||
forks: seq[TPosition]
|
||||
owner: PSym
|
||||
|
||||
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||
proc debugInfo(info: TLineInfo): string =
|
||||
result = $info.line #info.toFilename & ":" & $info.line
|
||||
|
||||
proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
|
||||
# for debugging purposes
|
||||
# first iteration: compute all necessary labels:
|
||||
var jumpTargets = initIntSet()
|
||||
let last = if last < 0: c.len-1 else: min(last, c.len-1)
|
||||
for i in start..last:
|
||||
if c[i].kind in {goto, fork}:
|
||||
if c[i].kind in {goto, fork, join}:
|
||||
jumpTargets.incl(i+c[i].dest)
|
||||
var i = start
|
||||
while i <= last:
|
||||
@@ -77,22 +80,27 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||
case c[i].kind
|
||||
of def, use:
|
||||
result.add renderTree(c[i].n)
|
||||
of goto, fork:
|
||||
of goto, fork, join:
|
||||
result.add "L"
|
||||
result.addInt c[i].dest+i
|
||||
result.add("\t#")
|
||||
result.add($c[i].n.info.line)
|
||||
result.add(debugInfo(c[i].n.info))
|
||||
result.add("\n")
|
||||
inc i
|
||||
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
||||
# consider calling `asciitables.alignTable`
|
||||
|
||||
proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
|
||||
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
|
||||
## echos the ControlFlowGraph for debugging purposes.
|
||||
echo codeListing(c, start, last).alignTable
|
||||
var buf = ""
|
||||
codeListing(c, buf, start, last)
|
||||
when declared(echo):
|
||||
echo buf
|
||||
|
||||
proc forkI(c: var Con; n: PNode): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
c.code.add Instr(n: n, kind: fork, dest: 0)
|
||||
c.forks.add result
|
||||
|
||||
proc gotoI(c: var Con; n: PNode): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
@@ -100,24 +108,6 @@ proc gotoI(c: var Con; n: PNode): TPosition =
|
||||
|
||||
#[
|
||||
|
||||
Join is no more
|
||||
===============
|
||||
Instead of generating join instructions we adapt our traversal of the CFG.
|
||||
|
||||
When encountering a fork we split into two paths, we follow the path
|
||||
starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
|
||||
If we encounter gotos that would jump further than the current joinpoint,
|
||||
as can happen with gotos generated by unstructured controlflow such as break, raise or return,
|
||||
we simply suspend following the current path, and follow the other path until the new joinpoint
|
||||
which is simply the instruction pointer returned to us by the now suspended path.
|
||||
If the path we are following now, also encounters a goto that exceeds the joinpoint
|
||||
we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
|
||||
If we eventually reach a common joinpoint we join the two paths.
|
||||
This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
|
||||
cutting down on the CFG size but is also mandatory for correctly handling complicated cases
|
||||
of unstructured controlflow.
|
||||
|
||||
|
||||
Design of join
|
||||
==============
|
||||
|
||||
@@ -266,36 +256,33 @@ duplicate the 'join' instructions on breaks and return exits!
|
||||
|
||||
]#
|
||||
|
||||
proc genLabel(c: Con): TPosition = TPosition(c.code.len)
|
||||
proc joinI(c: var Con; fromFork: TPosition; n: PNode) =
|
||||
let dist = fromFork.int - c.code.len
|
||||
c.code.add Instr(n: n, kind: join, dest: dist)
|
||||
|
||||
template checkedDistance(dist): int =
|
||||
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
|
||||
dist
|
||||
proc genLabel(c: Con): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
|
||||
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
|
||||
c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
|
||||
let dist = p.int - c.code.len
|
||||
doAssert(low(int) div 2 + 1 < dist and dist < high(int) div 2)
|
||||
c.code.add Instr(n: n, kind: goto, dest: dist)
|
||||
|
||||
proc patch(c: var Con, p: TPosition) =
|
||||
# patch with current index
|
||||
c.code[p.int].dest = checkedDistance(c.code.len - p.int)
|
||||
|
||||
proc gen(c: var Con; n: PNode)
|
||||
let p = p.int
|
||||
let diff = c.code.len - p
|
||||
doAssert(low(int) div 2 + 1 < diff and diff < high(int) div 2)
|
||||
c.code[p].dest = diff
|
||||
|
||||
proc popBlock(c: var Con; oldLen: int) =
|
||||
var exits: seq[TPosition]
|
||||
exits.add c.gotoI(newNode(nkEmpty))
|
||||
for f in c.blocks[oldLen].breakFixups:
|
||||
c.patch(f[0])
|
||||
for finale in f[1]:
|
||||
c.gen(finale)
|
||||
exits.add c.gotoI(newNode(nkEmpty))
|
||||
for e in exits:
|
||||
c.patch e
|
||||
for f in c.blocks[oldLen].fixups:
|
||||
c.patch(f)
|
||||
c.blocks.setLen(oldLen)
|
||||
|
||||
template withBlock(labl: PSym; body: untyped) =
|
||||
let oldLen = c.blocks.len
|
||||
c.blocks.add TBlock(isTryBlock: false, label: labl)
|
||||
template withBlock(labl: PSym; body: untyped) {.dirty.} =
|
||||
var oldLen {.gensym.} = c.blocks.len
|
||||
c.blocks.add TBlock(label: labl, fixups: @[])
|
||||
body
|
||||
popBlock(c, oldLen)
|
||||
|
||||
@@ -303,29 +290,25 @@ 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
|
||||
|
||||
proc gen(c: var Con; n: PNode) # {.noSideEffect.}
|
||||
|
||||
when true:
|
||||
proc genWhile(c: var Con; n: PNode) =
|
||||
# We unroll every loop 3 times. We emulate 0, 1, 2 iterations
|
||||
# through the loop. We need to prove this is correct for our
|
||||
# purposes. But Herb Sutter claims it is. (Proof by authority.)
|
||||
#
|
||||
# EDIT: Actually, we only need to unroll 2 times
|
||||
# because Nim doesn't have a way of breaking/goto-ing into
|
||||
# a loop iteration. Unrolling 2 times is much better for compile
|
||||
# times of nested loops than 3 times, so we do that here.
|
||||
#[
|
||||
while cond:
|
||||
body
|
||||
|
||||
Becomes:
|
||||
|
||||
block:
|
||||
if cond:
|
||||
body
|
||||
if cond:
|
||||
body
|
||||
if cond:
|
||||
body
|
||||
if cond:
|
||||
body
|
||||
|
||||
We still need to ensure 'break' resolves properly, so an AST to AST
|
||||
translation is impossible.
|
||||
@@ -348,23 +331,28 @@ when true:
|
||||
L4:
|
||||
join F1
|
||||
]#
|
||||
if isTrue(n[0]):
|
||||
if isTrue(n.sons[0]):
|
||||
# 'while true' is an idiom in Nim and so we produce
|
||||
# better code for it:
|
||||
withBlock(nil):
|
||||
for i in 0..1:
|
||||
c.gen(n[1])
|
||||
for i in 0..2:
|
||||
withBlock(nil):
|
||||
c.gen(n.sons[1])
|
||||
else:
|
||||
withBlock(nil):
|
||||
var endings: array[2, TPosition]
|
||||
for i in 0..1:
|
||||
c.gen(n[0])
|
||||
let oldForksLen = c.forks.len
|
||||
var endings: array[3, TPosition]
|
||||
for i in 0..2:
|
||||
withBlock(nil):
|
||||
c.gen(n.sons[0])
|
||||
endings[i] = c.forkI(n)
|
||||
c.gen(n[1])
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
c.gen(n.sons[1])
|
||||
for i in countdown(endings.high, 0):
|
||||
let endPos = endings[i]
|
||||
c.patch(endPos)
|
||||
c.joinI(c.forks.pop(), n)
|
||||
doAssert(c.forks.len == oldForksLen)
|
||||
|
||||
else:
|
||||
|
||||
proc genWhile(c: var Con; n: PNode) =
|
||||
# lab1:
|
||||
# cond, tmp
|
||||
@@ -372,21 +360,47 @@ else:
|
||||
# body
|
||||
# jmp lab1
|
||||
# lab2:
|
||||
let oldForksLen = c.forks.len
|
||||
let lab1 = c.genLabel
|
||||
withBlock(nil):
|
||||
if isTrue(n[0]):
|
||||
c.gen(n[1])
|
||||
if isTrue(n.sons[0]):
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, lab1)
|
||||
else:
|
||||
c.gen(n[0])
|
||||
forkT(n):
|
||||
c.gen(n[1])
|
||||
c.jmpBack(n, lab1)
|
||||
c.gen(n.sons[0])
|
||||
let lab2 = c.forkI(n)
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, lab1)
|
||||
c.patch(lab2)
|
||||
setLen(c.forks, oldForksLen)
|
||||
|
||||
proc genBlock(c: var Con; n: PNode) =
|
||||
withBlock(n.sons[0].sym):
|
||||
c.gen(n.sons[1])
|
||||
|
||||
proc genJoins(c: var Con; n: PNode) =
|
||||
for i in countdown(c.forks.high, 0): joinI(c, c.forks[i], n)
|
||||
|
||||
proc genBreak(c: var Con; n: PNode) =
|
||||
genJoins(c, n)
|
||||
let lab1 = c.gotoI(n)
|
||||
if n.sons[0].kind == nkSym:
|
||||
#echo cast[int](n.sons[0].sym)
|
||||
for i in countdown(c.blocks.len-1, 0):
|
||||
if c.blocks[i].label == n.sons[0].sym:
|
||||
c.blocks[i].fixups.add lab1
|
||||
return
|
||||
#globalError(n.info, "VM problem: cannot find 'break' target")
|
||||
else:
|
||||
c.blocks[c.blocks.high].fixups.add lab1
|
||||
|
||||
template forkT(n, body) =
|
||||
let oldLen = c.forks.len
|
||||
let lab1 = c.forkI(n)
|
||||
body
|
||||
c.patch(lab1)
|
||||
c.joinI(lab1, n)
|
||||
setLen(c.forks, oldLen)
|
||||
|
||||
proc genIf(c: var Con, n: PNode) =
|
||||
#[
|
||||
@@ -425,16 +439,21 @@ proc genIf(c: var Con, n: PNode) =
|
||||
join F1
|
||||
|
||||
]#
|
||||
let oldLen = c.forks.len
|
||||
var endings: seq[TPosition] = @[]
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
c.gen(it[0])
|
||||
for i in 0 ..< len(n):
|
||||
var it = n.sons[i]
|
||||
c.gen(it.sons[0])
|
||||
if it.len == 2:
|
||||
forkT(it[1]):
|
||||
c.gen(it[1])
|
||||
endings.add c.gotoI(it[1])
|
||||
let elsePos = forkI(c, it[1])
|
||||
c.gen(it.sons[1])
|
||||
endings.add(c.gotoI(it.sons[1]))
|
||||
c.patch(elsePos)
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
let endPos = endings[i]
|
||||
c.patch(endPos)
|
||||
c.joinI(c.forks.pop(), n)
|
||||
doAssert(c.forks.len == oldLen)
|
||||
|
||||
proc genAndOr(c: var Con; n: PNode) =
|
||||
# asgn dest, a
|
||||
@@ -442,9 +461,9 @@ proc genAndOr(c: var Con; n: PNode) =
|
||||
# asgn dest, b
|
||||
# lab1:
|
||||
# join F1
|
||||
c.gen(n[1])
|
||||
c.gen(n.sons[1])
|
||||
forkT(n):
|
||||
c.gen(n[2])
|
||||
c.gen(n.sons[2])
|
||||
|
||||
proc genCase(c: var Con; n: PNode) =
|
||||
# if (!expr1) goto lab1;
|
||||
@@ -457,112 +476,94 @@ proc genCase(c: var Con; n: PNode) =
|
||||
# lab2:
|
||||
# elsePart
|
||||
# Lend:
|
||||
let isExhaustive = skipTypes(n[0].typ,
|
||||
let isExhaustive = skipTypes(n.sons[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
||||
|
||||
# we generate endings as a set of chained gotos, this is a bit awkward but it
|
||||
# ensures when recursively traversing the CFG for various analysis, we don't
|
||||
# artificially extended the life of each branch (for the purposes of DFA)
|
||||
# beyond the minimum amount.
|
||||
var endings: seq[TPosition] = @[]
|
||||
c.gen(n[0])
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
if it.len == 1 or (i == n.len-1 and isExhaustive):
|
||||
let oldLen = c.forks.len
|
||||
c.gen(n.sons[0])
|
||||
for i in 1 ..< n.len:
|
||||
let it = n.sons[i]
|
||||
if it.len == 1:
|
||||
c.gen(it.sons[0])
|
||||
elif i == n.len-1 and isExhaustive:
|
||||
# treat the last branch as 'else' if this is an exhaustive case statement.
|
||||
c.gen(it.lastSon)
|
||||
if endings.len != 0:
|
||||
c.patch(endings[^1])
|
||||
else:
|
||||
forkT(it.lastSon):
|
||||
c.gen(it.lastSon)
|
||||
if endings.len != 0:
|
||||
c.patch(endings[^1])
|
||||
endings.add c.gotoI(it.lastSon)
|
||||
|
||||
proc genBlock(c: var Con; n: PNode) =
|
||||
withBlock(n[0].sym):
|
||||
c.gen(n[1])
|
||||
|
||||
proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
||||
let lab1 = c.gotoI(n)
|
||||
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:
|
||||
for b in countdown(c.blocks.high, i):
|
||||
if c.blocks[b].isTryBlock:
|
||||
trailingFinales.add c.blocks[b].finale
|
||||
|
||||
c.blocks[i].breakFixups.add (lab1, trailingFinales)
|
||||
|
||||
proc genBreak(c: var Con; n: PNode) =
|
||||
if n[0].kind == nkSym:
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
|
||||
genBreakOrRaiseAux(c, i, n)
|
||||
return
|
||||
#globalError(n.info, "VM problem: cannot find 'break' target")
|
||||
else:
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if not c.blocks[i].isTryBlock:
|
||||
genBreakOrRaiseAux(c, i, n)
|
||||
return
|
||||
let elsePos = c.forkI(it.lastSon)
|
||||
c.gen(it.lastSon)
|
||||
endings.add(c.gotoI(it.lastSon))
|
||||
c.patch(elsePos)
|
||||
for i in countdown(endings.high, 0):
|
||||
let endPos = endings[i]
|
||||
c.patch(endPos)
|
||||
c.joinI(c.forks.pop(), n)
|
||||
doAssert(c.forks.len == oldLen)
|
||||
|
||||
proc genTry(c: var Con; n: PNode) =
|
||||
let oldLen = c.forks.len
|
||||
var endings: seq[TPosition] = @[]
|
||||
|
||||
let oldLen = c.blocks.len
|
||||
c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
|
||||
|
||||
inc c.inTryStmt
|
||||
c.gen(n[0])
|
||||
let oldFixups = c.tryStmtFixups.len
|
||||
|
||||
#let elsePos = c.forkI(n)
|
||||
c.gen(n.sons[0])
|
||||
dec c.inTryStmt
|
||||
|
||||
for f in c.blocks[oldLen].raiseFixups:
|
||||
for i in oldFixups..c.tryStmtFixups.high:
|
||||
let f = c.tryStmtFixups[i]
|
||||
c.patch(f)
|
||||
# we also need to produce join instructions
|
||||
# for the 'fork' that might precede the goto instruction
|
||||
if f.int-1 >= 0 and c.code[f.int-1].kind == fork:
|
||||
c.joinI(TPosition(f.int-1), n)
|
||||
|
||||
c.blocks.setLen oldLen
|
||||
setLen(c.tryStmtFixups, oldFixups)
|
||||
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
#c.patch(elsePos)
|
||||
for i in 1 ..< n.len:
|
||||
let it = n.sons[i]
|
||||
if it.kind != nkFinally:
|
||||
forkT(it):
|
||||
c.gen(it.lastSon)
|
||||
endings.add c.gotoI(it)
|
||||
var blen = len(it)
|
||||
let endExcept = c.forkI(it)
|
||||
c.gen(it.lastSon)
|
||||
endings.add(c.gotoI(it))
|
||||
c.patch(endExcept)
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
let endPos = endings[i]
|
||||
c.patch(endPos)
|
||||
c.joinI(c.forks.pop(), n)
|
||||
|
||||
# join the 'elsePos' forkI instruction:
|
||||
#c.joinI(c.forks.pop(), n)
|
||||
|
||||
let fin = lastSon(n)
|
||||
if fin.kind == nkFinally:
|
||||
c.gen(fin[0])
|
||||
c.gen(fin.sons[0])
|
||||
doAssert(c.forks.len == oldLen)
|
||||
|
||||
template genNoReturn(c: var Con; n: PNode) =
|
||||
# leave the graph
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
|
||||
|
||||
proc genRaise(c: var Con; n: PNode) =
|
||||
gen(c, n[0])
|
||||
genJoins(c, n)
|
||||
gen(c, n.sons[0])
|
||||
if c.inTryStmt > 0:
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if c.blocks[i].isTryBlock:
|
||||
genBreakOrRaiseAux(c, i, n)
|
||||
return
|
||||
assert false #Unreachable
|
||||
c.tryStmtFixups.add c.gotoI(n)
|
||||
else:
|
||||
genNoReturn(c, n)
|
||||
|
||||
proc genImplicitReturn(c: var Con) =
|
||||
if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
|
||||
gen(c, c.owner.ast[resultPos])
|
||||
gen(c, c.owner.ast.sons[resultPos])
|
||||
|
||||
proc genReturn(c: var Con; n: PNode) =
|
||||
if n[0].kind != nkEmpty:
|
||||
gen(c, n[0])
|
||||
genJoins(c, n)
|
||||
if n.sons[0].kind != nkEmpty:
|
||||
gen(c, n.sons[0])
|
||||
else:
|
||||
genImplicitReturn(c)
|
||||
genBreakOrRaiseAux(c, 0, n)
|
||||
genNoReturn(c, n)
|
||||
|
||||
const
|
||||
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
||||
@@ -572,6 +573,16 @@ const
|
||||
nkObjDownConv, nkObjUpConv}
|
||||
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
|
||||
|
||||
proc getRoot(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of PathKinds0:
|
||||
result = result[0]
|
||||
of PathKinds1:
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc skipConvDfa*(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
@@ -582,97 +593,74 @@ proc skipConvDfa*(n: PNode): PNode =
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
type AliasKind* = enum
|
||||
yes, no, maybe
|
||||
proc genUse(c: var Con; orig: PNode) =
|
||||
let n = dfa.getRoot(orig)
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
c.code.add Instr(n: orig, kind: use, sym: if orig != n: nil else: n.sym)
|
||||
|
||||
proc aliases*(obj, field: PNode): AliasKind =
|
||||
# obj -> field:
|
||||
# x -> x: true
|
||||
# x -> x.f: true
|
||||
# x.f -> x: false
|
||||
# x.f -> x.f: true
|
||||
# x.f -> x.v: false
|
||||
# x -> x[0]: true
|
||||
# x[0] -> x: false
|
||||
# x[0] -> x[0]: true
|
||||
# x[0] -> x[1]: false
|
||||
# x -> x[i]: true
|
||||
# x[i] -> x: false
|
||||
# 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 n = n
|
||||
while true:
|
||||
case n.kind
|
||||
of PathKinds0 - {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
result.add n
|
||||
n = n[0]
|
||||
of nkSym:
|
||||
result.add n; break
|
||||
else: return no
|
||||
proc aliases(obj, field: PNode): bool =
|
||||
var n = field
|
||||
var obj = obj
|
||||
while obj.kind in {nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv}:
|
||||
obj = obj[0]
|
||||
while true:
|
||||
if sameTrees(obj, n): return true
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||
nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr, nkBracketExpr,
|
||||
nkHiddenDeref, nkDerefExpr:
|
||||
n = n[0]
|
||||
else:
|
||||
break
|
||||
return false
|
||||
|
||||
collectImportantNodes(objImportantNodes, obj)
|
||||
collectImportantNodes(fieldImportantNodes, field)
|
||||
proc useInstrTargets*(ins: Instr; loc: PNode): bool =
|
||||
assert ins.kind == use
|
||||
if ins.sym != nil and loc.kind == nkSym:
|
||||
result = ins.sym == loc.sym
|
||||
else:
|
||||
result = ins.n == loc or sameTrees(ins.n, loc)
|
||||
if not result:
|
||||
# We can come here if loc is 'x.f' and ins.n is 'x' or the other way round.
|
||||
# def x.f; question: does it affect the full 'x'? No.
|
||||
# def x; question: does it affect the 'x.f'? Yes.
|
||||
# use x.f; question: does it affect the full 'x'? No.
|
||||
# use x; question does it affect 'x.f'? Yes.
|
||||
result = aliases(ins.n, loc) or aliases(loc, ins.n)
|
||||
|
||||
# If field is less nested than obj, then it cannot be part of/aliased by obj
|
||||
if fieldImportantNodes.len < objImportantNodes.len: return no
|
||||
|
||||
result = yes
|
||||
for i in 1..objImportantNodes.len:
|
||||
# We compare the nodes leading to the location of obj and field
|
||||
# with each other.
|
||||
# We continue until they diverge, in which case we return no, or
|
||||
# until we reach the location of obj, in which case we do not need
|
||||
# to look further, since field must be part of/aliased by obj now.
|
||||
# If we encounter an element access using an index which is a runtime value,
|
||||
# we simply return maybe instead of yes; should further nodes not diverge.
|
||||
let currFieldPath = fieldImportantNodes[^i]
|
||||
let currObjPath = objImportantNodes[^i]
|
||||
|
||||
if currFieldPath.kind != currObjPath.kind:
|
||||
return no
|
||||
|
||||
case currFieldPath.kind
|
||||
of nkSym:
|
||||
if currFieldPath.sym != currObjPath.sym: return no
|
||||
of nkDotExpr:
|
||||
if currFieldPath[1].sym != currObjPath[1].sym: return no
|
||||
of nkCheckedFieldExpr:
|
||||
if currFieldPath[0][1].sym != currObjPath[0][1].sym: return no
|
||||
of nkBracketExpr:
|
||||
if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
|
||||
if currFieldPath[1].intVal != currObjPath[1].intVal:
|
||||
return no
|
||||
else:
|
||||
result = maybe
|
||||
else: assert false # unreachable
|
||||
proc defInstrTargets*(ins: Instr; loc: PNode): bool =
|
||||
assert ins.kind == def
|
||||
if ins.sym != nil and loc.kind == nkSym:
|
||||
result = ins.sym == loc.sym
|
||||
else:
|
||||
result = ins.n == loc or sameTrees(ins.n, loc)
|
||||
if not result:
|
||||
# We can come here if loc is 'x.f' and ins.n is 'x' or the other way round.
|
||||
# def x.f; question: does it affect the full 'x'? No.
|
||||
# def x; question: does it affect the 'x.f'? Yes.
|
||||
# use x.f; question: does it affect the full 'x'? No.
|
||||
# use x; question does it affect 'x.f'? Yes.
|
||||
result = aliases(ins.n, loc)
|
||||
|
||||
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||
var n = orig
|
||||
while true:
|
||||
case n.kind
|
||||
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||
nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr, nkBracketExpr:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
|
||||
# pointer indirection.
|
||||
# bug #14159, we cannot reason about sinkParam[].location as it can
|
||||
# still be shared for tyRef.
|
||||
n = n[0]
|
||||
return n.kind == nkSym and n.sym.owner == owner and
|
||||
(n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
|
||||
else: break
|
||||
return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or
|
||||
n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
|
||||
else:
|
||||
break
|
||||
# XXX Allow closure deref operations here if we know
|
||||
# the owner controlled the closure allocation?
|
||||
result = n.kind == nkSym and n.sym.owner == owner and
|
||||
{sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
|
||||
owner.kind != skModule and
|
||||
(n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
|
||||
# Note: There is a different move analyzer possible that checks for
|
||||
# consume(param.key); param.key = newValue for all paths. Then code like
|
||||
@@ -685,56 +673,45 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||
# free the old data and all is lost! Lesson: Don't be too smart, trust the
|
||||
# lower level C++ optimizer to specialize this code.
|
||||
|
||||
proc skipTrivials(c: var Con, n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of PathKinds0 - {nkBracketExpr}:
|
||||
result = result[0]
|
||||
of nkBracketExpr:
|
||||
gen(c, result[1])
|
||||
result = result[0]
|
||||
of PathKinds1:
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc genUse(c: var Con; orig: PNode) =
|
||||
let n = c.skipTrivials(orig)
|
||||
|
||||
if n.kind == nkSym:
|
||||
if n.sym.kind in InterestingSyms:
|
||||
c.code.add Instr(n: orig, kind: use)
|
||||
else:
|
||||
gen(c, n)
|
||||
|
||||
proc genDef(c: var Con; orig: PNode) =
|
||||
let n = c.skipTrivials(orig)
|
||||
|
||||
proc genDef(c: var Con; n: PNode) =
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
c.code.add Instr(n: orig, kind: def)
|
||||
c.code.add Instr(n: n, kind: def, sym: n.sym)
|
||||
elif isAnalysableFieldAccess(n, c.owner):
|
||||
c.code.add Instr(n: n, kind: def, sym: nil)
|
||||
|
||||
proc canRaise(fn: PNode): bool =
|
||||
const magicsThatCanRaise = {
|
||||
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst}
|
||||
if fn.kind == nkSym and fn.sym.magic notin magicsThatCanRaise:
|
||||
result = false
|
||||
else:
|
||||
result = true
|
||||
|
||||
proc genCall(c: var Con; n: PNode) =
|
||||
gen(c, n[0])
|
||||
var t = n[0].typ
|
||||
if t != nil: t = t.skipTypes(abstractInst)
|
||||
inc c.inCall
|
||||
for i in 1..<n.len:
|
||||
gen(c, n[i])
|
||||
when false:
|
||||
if t != nil and i < t.len and t[i].kind == tyOut:
|
||||
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
|
||||
if t != nil and i < t.len and t.sons[i].kind == tyVar:
|
||||
# This is wrong! Pass by var is a 'might def', not a 'must def'
|
||||
# like the other defs we emit. This is not good enough for a move
|
||||
# optimizer.
|
||||
genDef(c, n[i])
|
||||
# every call can potentially raise:
|
||||
if c.inTryStmt > 0 and canRaiseConservative(n[0]):
|
||||
if c.inTryStmt > 0 and canRaise(n[0]):
|
||||
# we generate the instruction sequence:
|
||||
# fork lab1
|
||||
# goto exceptionHandler (except or finally)
|
||||
# lab1:
|
||||
# join F1
|
||||
forkT(n):
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if c.blocks[i].isTryBlock:
|
||||
genBreakOrRaiseAux(c, i, n)
|
||||
break
|
||||
let endGoto = c.forkI(n)
|
||||
c.tryStmtFixups.add c.gotoI(n)
|
||||
c.patch(endGoto)
|
||||
c.joinI(c.forks.pop(), n)
|
||||
dec c.inCall
|
||||
|
||||
proc genMagic(c: var Con; n: PNode; m: TMagic) =
|
||||
case m
|
||||
@@ -751,45 +728,39 @@ proc genVarSection(c: var Con; n: PNode) =
|
||||
discard
|
||||
elif a.kind == nkVarTuple:
|
||||
gen(c, a.lastSon)
|
||||
for i in 0..<a.len-2: genDef(c, a[i])
|
||||
for i in 0 .. a.len-3: genDef(c, a[i])
|
||||
else:
|
||||
gen(c, a.lastSon)
|
||||
if a.lastSon.kind != nkEmpty:
|
||||
genDef(c, a[0])
|
||||
genDef(c, a.sons[0])
|
||||
|
||||
proc gen(c: var Con; n: PNode) =
|
||||
case n.kind
|
||||
of nkSym: genUse(c, n)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym:
|
||||
let s = n[0].sym
|
||||
if n.sons[0].kind == nkSym:
|
||||
let s = n.sons[0].sym
|
||||
if s.magic != mNone:
|
||||
genMagic(c, n, s.magic)
|
||||
else:
|
||||
genCall(c, n)
|
||||
if sfNoReturn in n[0].sym.flags:
|
||||
if sfNoReturn in n.sons[0].sym.flags:
|
||||
genNoReturn(c, n)
|
||||
else:
|
||||
genCall(c, n)
|
||||
of nkCharLit..nkNilLit: discard
|
||||
of nkAsgn, nkFastAsgn:
|
||||
gen(c, n[1])
|
||||
|
||||
if n[0].kind in PathKinds0:
|
||||
let a = c.skipTrivials(n[0])
|
||||
if a.kind in nkCallKinds:
|
||||
gen(c, a)
|
||||
|
||||
# watch out: 'obj[i].f2 = value' sets 'f2' but
|
||||
# "uses" 'i'. But we are only talking about builtin array indexing so
|
||||
# it doesn't matter and 'x = 34' is NOT a usage of 'x'.
|
||||
genDef(c, n[0])
|
||||
of PathKinds0 - {nkObjDownConv, nkObjUpConv}:
|
||||
of PathKinds0 - {nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}:
|
||||
genUse(c, n)
|
||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||
of nkWhenStmt:
|
||||
# This is "when nimvm" node. Chose the first branch.
|
||||
gen(c, n[0][1])
|
||||
gen(c, n.sons[0].sons[1])
|
||||
of nkCaseStmt: genCase(c, n)
|
||||
of nkWhileStmt: genWhile(c, n)
|
||||
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
|
||||
@@ -801,21 +772,18 @@ proc gen(c: var Con; n: PNode) =
|
||||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
|
||||
for x in n: gen(c, x)
|
||||
of nkPragmaBlock: gen(c, n.lastSon)
|
||||
of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
|
||||
gen(c, n[0])
|
||||
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
|
||||
gen(c, n[1])
|
||||
of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n.sons[0])
|
||||
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, nkHiddenSubConv, nkHiddenStdConv:
|
||||
gen(c, n.sons[1])
|
||||
of nkStringToCString, nkCStringToString: gen(c, n.sons[0])
|
||||
of nkVarSection, nkLetSection: genVarSection(c, n)
|
||||
of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
|
||||
of nkDefer:
|
||||
doAssert false, "dfa construction pass requires the elimination of 'defer'"
|
||||
else: discard
|
||||
|
||||
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
|
||||
## constructs a control flow graph for ``body``.
|
||||
var c = Con(code: @[], blocks: @[], owner: s)
|
||||
withBlock(s):
|
||||
gen(c, body)
|
||||
genImplicitReturn(c)
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
result = c.code # will move
|
||||
else:
|
||||
shallowCopy(result, c.code)
|
||||
gen(c, body)
|
||||
genImplicitReturn(c)
|
||||
shallowCopy(result, c.code)
|
||||
|
||||
1387
compiler/docgen.nim
1387
compiler/docgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
# semantic checking.
|
||||
|
||||
import
|
||||
options, ast, msgs, passes, docgen, lineinfos, pathutils, packages
|
||||
options, ast, msgs, idents, passes, docgen, lineinfos, pathutils
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
@@ -22,16 +22,14 @@ type
|
||||
config: ConfigRef
|
||||
PGen = ref TGen
|
||||
|
||||
proc shouldProcess(g: PGen): bool =
|
||||
(optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or
|
||||
template shouldProcess(g): bool =
|
||||
(g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
|
||||
sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
|
||||
|
||||
template closeImpl(body: untyped) {.dirty.} =
|
||||
var g = PGen(p)
|
||||
let useWarning = sfMainModule notin g.module.flags
|
||||
let groupedToc = true
|
||||
if shouldProcess(g):
|
||||
finishGenerateDoc(g.doc)
|
||||
body
|
||||
try:
|
||||
generateIndex(g.doc)
|
||||
@@ -40,7 +38,7 @@ template closeImpl(body: untyped) {.dirty.} =
|
||||
|
||||
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
closeImpl:
|
||||
writeOutput(g.doc, useWarning, groupedToc)
|
||||
writeOutput(g.doc, useWarning)
|
||||
|
||||
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
closeImpl:
|
||||
@@ -69,18 +67,13 @@ template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
g.doc = d
|
||||
result = g
|
||||
|
||||
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
myOpenImpl(HtmlExt)
|
||||
|
||||
proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
myOpenImpl(TexExt)
|
||||
|
||||
proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
proc myOpenJson(graph: ModuleGraph; module: PSym): 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)
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
|
||||
import ast, idents, lineinfos, modulegraphs, magicsys
|
||||
|
||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), result, info)
|
||||
dest.typ = t
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
|
||||
res.typ = getSysType(g, info, tyString)
|
||||
|
||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
||||
result.typ = newType(tyProc, t.owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
addSon(result.typ.n, newNodeI(nkEffectList, info))
|
||||
|
||||
result.typ.addParam dest
|
||||
|
||||
@@ -22,7 +22,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
caseStmt.add(newSymNode dest)
|
||||
|
||||
# copy the branches over, but replace the fields with the for loop body:
|
||||
for i in 0..<t.n.len:
|
||||
for i in 0 ..< t.n.len:
|
||||
assert(t.n[i].kind == nkSym)
|
||||
var field = t.n[i].sym
|
||||
let val = if field.ast == nil: field.name.s else: field.ast.strVal
|
||||
@@ -33,48 +33,41 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
body.add(caseStmt)
|
||||
|
||||
var n = newNodeI(nkProcDef, info, bodyPos+2)
|
||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
||||
n[namePos] = newSymNode(result)
|
||||
n[paramsPos] = result.typ.n
|
||||
n[bodyPos] = body
|
||||
n[resultPos] = newSymNode(res)
|
||||
for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
|
||||
n.sons[namePos] = newSymNode(result)
|
||||
n.sons[paramsPos] = result.typ.n
|
||||
n.sons[bodyPos] = body
|
||||
n.sons[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfNeverRaises
|
||||
|
||||
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
||||
proc searchObjCase(obj: PNode; field: PSym): PNode =
|
||||
case obj.kind
|
||||
of nkSym:
|
||||
result = nil
|
||||
of nkElse, nkOfBranch:
|
||||
result = searchObjCaseImpl(obj.lastSon, field)
|
||||
result = searchObjCase(obj.lastSon, field)
|
||||
else:
|
||||
if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
|
||||
result = obj
|
||||
else:
|
||||
for x in obj:
|
||||
result = searchObjCaseImpl(x, field)
|
||||
result = searchObjCase(x, field)
|
||||
if result != nil: break
|
||||
|
||||
proc searchObjCase(t: PType; field: PSym): PNode =
|
||||
result = searchObjCaseImpl(t.n, 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): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), t.owner, info)
|
||||
|
||||
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), result, info)
|
||||
dest.typ = field.typ
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
|
||||
res.typ = getSysType(g, info, tyUInt8)
|
||||
|
||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
||||
result.typ = newType(tyProc, t.owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
addSon(result.typ.n, newNodeI(nkEffectList, info))
|
||||
|
||||
result.typ.addParam dest
|
||||
|
||||
@@ -82,11 +75,12 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
|
||||
var caseStmt = newNodeI(nkCaseStmt, info)
|
||||
caseStmt.add(newSymNode dest)
|
||||
|
||||
let subObj = searchObjCase(t, field)
|
||||
for i in 1..<subObj.len:
|
||||
let subObj = searchObjCase(t.n, field)
|
||||
doAssert subObj != nil
|
||||
for i in 1 ..< subObj.len:
|
||||
let ofBranch = subObj[i]
|
||||
var newBranch = newNodeI(ofBranch.kind, ofBranch.info)
|
||||
for j in 0..<ofBranch.len-1:
|
||||
for j in 0..ofBranch.len-2:
|
||||
newBranch.add ofBranch[j]
|
||||
|
||||
newBranch.add newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newIntNode(nkInt8Lit, i)))
|
||||
@@ -95,11 +89,10 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
|
||||
body.add(caseStmt)
|
||||
|
||||
var n = newNodeI(nkProcDef, info, bodyPos+2)
|
||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
||||
n[namePos] = newSymNode(result)
|
||||
n[paramsPos] = result.typ.n
|
||||
n[bodyPos] = body
|
||||
n[resultPos] = newSymNode(res)
|
||||
for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
|
||||
n.sons[namePos] = newSymNode(result)
|
||||
n.sons[paramsPos] = result.typ.n
|
||||
n.sons[bodyPos] = body
|
||||
n.sons[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfNeverRaises
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2021 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains support code for new-styled error
|
||||
## handling via an `nkError` node kind.
|
||||
|
||||
import ast, renderer, options, strutils, types
|
||||
|
||||
type
|
||||
ErrorKind* = enum ## expand as you need.
|
||||
RawTypeMismatchError
|
||||
ExpressionCannotBeCalled
|
||||
CustomError
|
||||
WrongNumberOfArguments
|
||||
AmbiguousCall
|
||||
|
||||
proc errorSubNode*(n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
result = nil
|
||||
of nkError:
|
||||
result = n
|
||||
else:
|
||||
result = nil
|
||||
for i in 0..<n.len:
|
||||
result = errorSubNode(n[i])
|
||||
if result != nil: break
|
||||
|
||||
proc newError*(wrongNode: PNode; k: ErrorKind; args: varargs[PNode]): PNode =
|
||||
assert wrongNode.kind != nkError
|
||||
let innerError = errorSubNode(wrongNode)
|
||||
if innerError != nil:
|
||||
return innerError
|
||||
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
|
||||
|
||||
proc newError*(wrongNode: PNode; msg: string): PNode =
|
||||
assert wrongNode.kind != nkError
|
||||
let innerError = errorSubNode(wrongNode)
|
||||
if innerError != nil:
|
||||
return innerError
|
||||
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)
|
||||
|
||||
proc errorToString*(config: ConfigRef; n: PNode): string =
|
||||
assert n.kind == nkError
|
||||
assert n.len > 1
|
||||
let wrongNode = n[0]
|
||||
case ErrorKind(n[1].intVal)
|
||||
of RawTypeMismatchError:
|
||||
result = "type mismatch"
|
||||
of ExpressionCannotBeCalled:
|
||||
result = "expression '$1' cannot be called" % wrongNode[0].renderTree
|
||||
of CustomError:
|
||||
result = n[2].strVal
|
||||
of WrongNumberOfArguments:
|
||||
result = "wrong number of arguments"
|
||||
of AmbiguousCall:
|
||||
let a = n[2].sym
|
||||
let b = n[3].sym
|
||||
var args = "("
|
||||
for i in 1..<wrongNode.len:
|
||||
if i > 1: args.add(", ")
|
||||
args.add(typeToString(wrongNode[i].typ))
|
||||
args.add(")")
|
||||
result = "ambiguous call; both $1 and $2 match for: $3" % [
|
||||
getProcHeader(config, a),
|
||||
getProcHeader(config, b),
|
||||
args]
|
||||
@@ -9,18 +9,13 @@
|
||||
|
||||
## This file implements the FFI part of the evaluator for Nim code.
|
||||
|
||||
import ast, types, options, tables, dynlib, msgs, lineinfos
|
||||
from os import getAppFilename
|
||||
import ast, astalgo, ropes, types, options, tables, dynlib, msgs, os, lineinfos
|
||||
import pkg/libffi
|
||||
|
||||
when defined(windows):
|
||||
const libcDll = "msvcrt.dll"
|
||||
elif defined(linux):
|
||||
const libcDll = "libc.so(.6|.5|)"
|
||||
elif defined(openbsd):
|
||||
const libcDll = "/usr/lib/libc.so(.95.1|)"
|
||||
elif defined(bsd):
|
||||
const libcDll = "/lib/libc.so.7"
|
||||
elif defined(osx):
|
||||
const libcDll = "/usr/lib/libSystem.dylib"
|
||||
else:
|
||||
@@ -32,7 +27,7 @@ var
|
||||
gDllCache = initTable[string, LibHandle]()
|
||||
|
||||
when defined(windows):
|
||||
var gExeHandle = loadLib(getAppFilename())
|
||||
var gExeHandle = loadLib(os.getAppFilename())
|
||||
else:
|
||||
var gExeHandle = loadLib()
|
||||
|
||||
@@ -51,19 +46,24 @@ proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo)
|
||||
const
|
||||
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
|
||||
|
||||
var myerrno {.importc: "errno", header: "<errno.h>".}: cint ## error variable
|
||||
|
||||
proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
|
||||
let name = sym.cname # $sym.loc.r would point to internal name
|
||||
let name = $sym.loc.r
|
||||
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
||||
# that contains the address instead:
|
||||
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
||||
when true:
|
||||
var libPathMsg = ""
|
||||
case name
|
||||
of "stdin": result.intVal = cast[ByteAddress](system.stdin)
|
||||
of "stdout": result.intVal = cast[ByteAddress](system.stdout)
|
||||
of "stderr": result.intVal = cast[ByteAddress](system.stderr)
|
||||
of "vmErrnoWrapper": result.intVal = cast[ByteAddress](myerrno)
|
||||
else:
|
||||
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
|
||||
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
|
||||
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
|
||||
# first try this exe itself:
|
||||
theAddr = gExeHandle.symAddr(name)
|
||||
# then try libc:
|
||||
@@ -72,14 +72,12 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
elif not lib.isNil:
|
||||
let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
|
||||
libPathMsg = dll
|
||||
let dllhandle = getDll(conf, gDllCache, dll, sym.info)
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
if theAddr.isNil: globalError(conf, sym.info,
|
||||
"cannot import symbol: " & name & " from " & libPathMsg)
|
||||
if theAddr.isNil: globalError(conf, sym.info, "cannot import: " & sym.name.s)
|
||||
result.intVal = cast[ByteAddress](theAddr)
|
||||
|
||||
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
||||
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.TType =
|
||||
if t == nil: return addr libffi.type_void
|
||||
|
||||
case t.kind
|
||||
@@ -92,11 +90,11 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
||||
else: result = nil
|
||||
of tyFloat, tyFloat64: result = addr libffi.type_double
|
||||
of tyFloat32: result = addr libffi.type_float
|
||||
of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCstring, tySequence, tyString, tyUntyped,
|
||||
of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyUntyped,
|
||||
tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
|
||||
result = addr libffi.type_pointer
|
||||
of tyDistinct, tyAlias, tySink:
|
||||
result = mapType(conf, t[0])
|
||||
result = mapType(conf, t.sons[0])
|
||||
else:
|
||||
result = nil
|
||||
# too risky:
|
||||
@@ -104,14 +102,14 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
||||
|
||||
proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI =
|
||||
case cc
|
||||
of ccNimCall: result = DEFAULT_ABI
|
||||
of ccDefault: result = DEFAULT_ABI
|
||||
of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
|
||||
of ccCDecl: result = DEFAULT_ABI
|
||||
else:
|
||||
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 rd(T, p: untyped): untyped = (cast[ptr T](p))[]
|
||||
template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
|
||||
template `+!`(x, y: untyped): untyped =
|
||||
cast[pointer](cast[ByteAddress](x) + y)
|
||||
|
||||
@@ -122,16 +120,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.lastSon)
|
||||
result = sizeof(pointer) + packSize(conf, v.sons[0], typ.lastSon)
|
||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||
result = packSize(conf, v, typ[0])
|
||||
result = packSize(conf, v, typ.sons[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[1])
|
||||
result = v.len * packSize(conf, v.sons[0], typ.sons[1])
|
||||
else:
|
||||
result = getSize(conf, typ).int
|
||||
|
||||
@@ -140,16 +138,16 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
|
||||
proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
result = getField(conf, n[i], position)
|
||||
for i in 0 ..< len(n):
|
||||
result = getField(conf, n.sons[i], position)
|
||||
if result != nil: return
|
||||
of nkRecCase:
|
||||
result = getField(conf, n[0], position)
|
||||
result = getField(conf, n.sons[0], position)
|
||||
if result != nil: return
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
for i in 1 ..< len(n):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = getField(conf, lastSon(n[i]), position)
|
||||
result = getField(conf, lastSon(n.sons[i]), position)
|
||||
if result != nil: return
|
||||
else: internalError(conf, n.info, "getField(record case branch)")
|
||||
of nkSym:
|
||||
@@ -160,12 +158,12 @@ proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
|
||||
internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
|
||||
# compute the field's offsets:
|
||||
discard getSize(conf, typ)
|
||||
for i in ord(x.kind == nkObjConstr)..<x.len:
|
||||
var it = x[i]
|
||||
for i in ord(x.kind == nkObjConstr) ..< len(x):
|
||||
var it = x.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
internalAssert conf, it[0].kind == nkSym
|
||||
let field = it[0].sym
|
||||
pack(conf, it[1], field.typ, res +! field.offset)
|
||||
internalAssert conf, it.sons[0].kind == nkSym
|
||||
let field = it.sons[0].sym
|
||||
pack(conf, it.sons[1], field.typ, res +! field.offset)
|
||||
elif typ.n != nil:
|
||||
let field = getField(conf, typ.n, i)
|
||||
pack(conf, it, field.typ, res +! field.offset)
|
||||
@@ -177,8 +175,8 @@ const maxPackDepth = 20
|
||||
var packRecCheck = 0
|
||||
|
||||
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
|
||||
template awr(typ, v: untyped): untyped =
|
||||
wr(typ, res, v)
|
||||
template awr(T, v: untyped): untyped =
|
||||
wr(T, res, v)
|
||||
|
||||
case typ.kind
|
||||
of tyBool: awr(bool, v.intVal != 0)
|
||||
@@ -205,7 +203,7 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
|
||||
of tyFloat32: awr(float32, v.floatVal)
|
||||
of tyFloat64: awr(float64, v.floatVal)
|
||||
|
||||
of tyPointer, tyProc, tyCstring, tyString:
|
||||
of tyPointer, tyProc, tyCString, tyString:
|
||||
if v.kind == nkNilLit:
|
||||
# nothing to do since the memory is 0 initialized anyway
|
||||
discard
|
||||
@@ -226,19 +224,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.lastSon, res +! sizeof(pointer))
|
||||
pack(conf, v.sons[0], typ.lastSon, res +! sizeof(pointer))
|
||||
dec packRecCheck
|
||||
awr(pointer, res +! sizeof(pointer))
|
||||
of tyArray:
|
||||
let baseSize = getSize(conf, typ[1])
|
||||
for i in 0..<v.len:
|
||||
pack(conf, v[i], typ[1], res +! i * baseSize)
|
||||
let baseSize = getSize(conf, typ.sons[1])
|
||||
for i in 0 ..< v.len:
|
||||
pack(conf, v.sons[i], typ.sons[1], res +! i * baseSize)
|
||||
of tyObject, tyTuple:
|
||||
packObject(conf, v, typ, res)
|
||||
of tyNil:
|
||||
discard
|
||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||
pack(conf, v, typ[0], res)
|
||||
pack(conf, v, typ.sons[0], res)
|
||||
else:
|
||||
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
|
||||
@@ -247,14 +245,14 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode
|
||||
proc unpackObjectAdd(conf: ConfigRef, x: pointer, n, result: PNode) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
unpackObjectAdd(conf, x, n[i], result)
|
||||
for i in 0 ..< len(n):
|
||||
unpackObjectAdd(conf, x, n.sons[i], result)
|
||||
of nkRecCase:
|
||||
globalError(conf, result.info, "case objects cannot be unpacked")
|
||||
of nkSym:
|
||||
var pair = newNodeI(nkExprColonExpr, result.info, 2)
|
||||
pair[0] = n
|
||||
pair[1] = unpack(conf, x +! n.sym.offset, n.sym.typ, nil)
|
||||
pair.sons[0] = n
|
||||
pair.sons[1] = unpack(conf, x +! n.sym.offset, n.sym.typ, nil)
|
||||
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
|
||||
result.add pair
|
||||
else: discard
|
||||
@@ -277,15 +275,15 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
globalError(conf, n.info, "cannot map value from FFI")
|
||||
if typ.n.isNil:
|
||||
globalError(conf, n.info, "cannot unpack unnamed tuple")
|
||||
for i in ord(n.kind == nkObjConstr)..<n.len:
|
||||
var it = n[i]
|
||||
for i in ord(n.kind == nkObjConstr) ..< len(n):
|
||||
var it = n.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
internalAssert conf, it[0].kind == nkSym
|
||||
let field = it[0].sym
|
||||
it[1] = unpack(conf, x +! field.offset, field.typ, it[1])
|
||||
internalAssert conf, it.sons[0].kind == nkSym
|
||||
let field = it.sons[0].sym
|
||||
it.sons[1] = unpack(conf, x +! field.offset, field.typ, it.sons[1])
|
||||
else:
|
||||
let field = getField(conf, typ.n, i)
|
||||
n[i] = unpack(conf, x +! field.offset, field.typ, it)
|
||||
n.sons[i] = unpack(conf, x +! field.offset, field.typ, it)
|
||||
|
||||
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
if n.isNil:
|
||||
@@ -296,9 +294,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[1])
|
||||
for i in 0..<result.len:
|
||||
result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
|
||||
let baseSize = getSize(conf, typ.sons[1])
|
||||
for i in 0 ..< result.len:
|
||||
result.sons[i] = unpack(conf, x +! i * baseSize, typ.sons[1], result.sons[i])
|
||||
|
||||
proc canonNodeKind(k: TNodeKind): TNodeKind =
|
||||
case k
|
||||
@@ -329,7 +327,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
else:
|
||||
reset n[]
|
||||
result = n
|
||||
result[] = TNode(kind: nkNilLit)
|
||||
result.kind = nkNilLit
|
||||
result.typ = typ
|
||||
|
||||
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
|
||||
@@ -378,7 +376,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
awi(nkPtrLit, cast[ByteAddress](p))
|
||||
elif n != nil and n.len == 1:
|
||||
internalAssert(conf, n.kind == nkRefTy)
|
||||
n[0] = unpack(conf, p, typ.lastSon, n[0])
|
||||
n.sons[0] = unpack(conf, p, typ.lastSon, n.sons[0])
|
||||
result = n
|
||||
else:
|
||||
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
@@ -386,7 +384,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
result = unpackObject(conf, x, typ, n)
|
||||
of tyArray:
|
||||
result = unpackArray(conf, x, typ, n)
|
||||
of tyCstring, tyString:
|
||||
of tyCString, tyString:
|
||||
let p = rd(cstring, x)
|
||||
if p.isNil:
|
||||
setNil()
|
||||
@@ -402,7 +400,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
|
||||
proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
|
||||
tyProc, tyCstring, tyString,
|
||||
tyProc, tyCString, tyString,
|
||||
tySequence}:
|
||||
result = newNodeIT(x.kind, x.info, destTyp)
|
||||
result.intVal = x.intVal
|
||||
@@ -421,41 +419,41 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
dealloc a
|
||||
|
||||
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
|
||||
internalAssert conf, call[0].kind == nkPtrLit
|
||||
internalAssert conf, call.sons[0].kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
var sig: TParamList
|
||||
# use the arguments' types for varargs support:
|
||||
for i in 1..<call.len:
|
||||
sig[i-1] = mapType(conf, call[i].typ)
|
||||
for i in 1..call.len-1:
|
||||
sig[i-1] = mapType(conf, call.sons[i].typ)
|
||||
if sig[i-1].isNil:
|
||||
globalError(conf, call.info, "cannot map FFI type")
|
||||
|
||||
let typ = call[0].typ
|
||||
let typ = call.sons[0].typ
|
||||
if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
|
||||
mapType(conf, typ[0]), sig) != OK:
|
||||
mapType(conf, typ.sons[0]), sig) != OK:
|
||||
globalError(conf, call.info, "error in FFI call")
|
||||
|
||||
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[0]): pointer(nil)
|
||||
else: alloc(getSize(conf, typ[0]).int)
|
||||
var args: TArgList
|
||||
let fn = cast[pointer](call.sons[0].intVal)
|
||||
for i in 1 .. call.len-1:
|
||||
var t = call.sons[i].typ
|
||||
args[i-1] = alloc0(packSize(conf, call.sons[i], t))
|
||||
pack(conf, call.sons[i], t, args[i-1])
|
||||
let retVal = if isEmptyType(typ.sons[0]): pointer(nil)
|
||||
else: alloc(getSize(conf, typ.sons[0]).int)
|
||||
|
||||
libffi.call(cif, fn, retVal, args)
|
||||
|
||||
if retVal.isNil:
|
||||
result = newNode(nkEmpty)
|
||||
else:
|
||||
result = unpack(conf, retVal, typ[0], nil)
|
||||
result = unpack(conf, retVal, typ.sons[0], nil)
|
||||
result.info = call.info
|
||||
|
||||
if retVal != nil: dealloc retVal
|
||||
for i in 1..<call.len:
|
||||
call[i] = unpack(conf, args[i-1], typ[i], call[i])
|
||||
for i in 1 .. call.len-1:
|
||||
call.sons[i] = unpack(conf, args[i-1], typ.sons[i], call[i])
|
||||
dealloc args[i-1]
|
||||
|
||||
proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
@@ -464,39 +462,39 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
internalAssert conf, fn.kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
var sig: TParamList
|
||||
for i in 0..len-1:
|
||||
var aTyp = args[i+start].typ
|
||||
if aTyp.isNil:
|
||||
internalAssert conf, i+1 < fntyp.len
|
||||
aTyp = fntyp[i+1]
|
||||
aTyp = fntyp.sons[i+1]
|
||||
args[i+start].typ = aTyp
|
||||
sig[i] = mapType(conf, aTyp)
|
||||
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
|
||||
|
||||
if prep_cif(cif, mapCallConv(conf, fntyp.callConv, info), cuint(len),
|
||||
mapType(conf, fntyp[0]), sig) != OK:
|
||||
mapType(conf, fntyp.sons[0]), sig) != OK:
|
||||
globalError(conf, info, "error in FFI call")
|
||||
|
||||
var cargs: ArgList
|
||||
var cargs: TArgList
|
||||
let fn = cast[pointer](fn.intVal)
|
||||
for i in 0..len-1:
|
||||
for i in 0 .. len-1:
|
||||
let t = args[i+start].typ
|
||||
cargs[i] = alloc0(packSize(conf, args[i+start], t))
|
||||
pack(conf, args[i+start], t, cargs[i])
|
||||
let retVal = if isEmptyType(fntyp[0]): pointer(nil)
|
||||
else: alloc(getSize(conf, fntyp[0]).int)
|
||||
let retVal = if isEmptyType(fntyp.sons[0]): pointer(nil)
|
||||
else: alloc(getSize(conf, fntyp.sons[0]).int)
|
||||
|
||||
libffi.call(cif, fn, retVal, cargs)
|
||||
|
||||
if retVal.isNil:
|
||||
result = newNode(nkEmpty)
|
||||
else:
|
||||
result = unpack(conf, retVal, fntyp[0], nil)
|
||||
result = unpack(conf, retVal, fntyp.sons[0], nil)
|
||||
result.info = info
|
||||
|
||||
if retVal != nil: dealloc retVal
|
||||
for i in 0..len-1:
|
||||
for i in 0 .. len-1:
|
||||
let t = args[i+start].typ
|
||||
args[i+start] = unpack(conf, cargs[i], t, args[i+start])
|
||||
dealloc cargs[i]
|
||||
|
||||
@@ -21,8 +21,6 @@ type
|
||||
# new symbol
|
||||
config: ConfigRef
|
||||
ic: IdentCache
|
||||
instID: int
|
||||
idgen: IdGenerator
|
||||
|
||||
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
||||
result = copyNode(a)
|
||||
@@ -41,22 +39,22 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
var s = templ.sym
|
||||
if (s.owner == nil and s.kind == skParam) or s.owner == c.owner:
|
||||
if s.kind == skParam and {sfGenSym, sfTemplateParam} * s.flags == {sfTemplateParam}:
|
||||
handleParam actual[s.position]
|
||||
handleParam actual.sons[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.len + s.position - 1]
|
||||
handleParam actual.sons[s.owner.typ.len + s.position - 1]
|
||||
else:
|
||||
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
|
||||
var x = PSym(idTableGet(c.mapping, s))
|
||||
if x == nil:
|
||||
x = copySym(s, nextSymId(c.idgen))
|
||||
x = copySym(s)
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
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:
|
||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
|
||||
if sfGenSym in s.flags and optNimV019 notin c.config.globalOptions:
|
||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id),
|
||||
if c.instLines: actual.info else: templ.info)
|
||||
else:
|
||||
result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
|
||||
@@ -70,8 +68,8 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
# "declarative" context (bug #9235).
|
||||
if c.isDeclarative:
|
||||
var res = copyNode(c, templ, actual)
|
||||
for i in 0..<templ.len:
|
||||
evalTemplateAux(templ[i], actual, c, res)
|
||||
for i in 0 ..< len(templ):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
else:
|
||||
result.add newNodeI(nkEmpty, templ.info)
|
||||
@@ -83,14 +81,10 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
not c.isDeclarative:
|
||||
c.isDeclarative = true
|
||||
isDeclarative = true
|
||||
if (not c.isDeclarative) and templ.kind in nkCallKinds and isRunnableExamples(templ[0]):
|
||||
# fixes bug #16993, bug #18054
|
||||
discard
|
||||
else:
|
||||
var res = copyNode(c, templ, actual)
|
||||
for i in 0..<templ.len:
|
||||
evalTemplateAux(templ[i], actual, c, res)
|
||||
result.add res
|
||||
var res = copyNode(c, templ, actual)
|
||||
for i in 0 ..< len(templ):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
if isDeclarative: c.isDeclarative = false
|
||||
|
||||
const
|
||||
@@ -128,22 +122,22 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
|
||||
n.renderTree)
|
||||
|
||||
result = newNodeI(nkArgList, n.info)
|
||||
for i in 1..givenRegularParams:
|
||||
result.add n[i]
|
||||
for i in 1 .. givenRegularParams:
|
||||
result.addSon n[i]
|
||||
|
||||
# handle parameters with default values, which were
|
||||
# not supplied by the user
|
||||
for i in givenRegularParams+1..expectedRegularParams:
|
||||
let default = s.typ.n[i].sym.ast
|
||||
for i in givenRegularParams+1 .. expectedRegularParams:
|
||||
let default = s.typ.n.sons[i].sym.ast
|
||||
if default.isNil or default.kind == nkEmpty:
|
||||
localError(conf, n.info, errWrongNumberOfArguments)
|
||||
result.add newNodeI(nkEmpty, n.info)
|
||||
addSon(result, newNodeI(nkEmpty, n.info))
|
||||
else:
|
||||
result.add default.copyTree
|
||||
addSon(result, default.copyTree)
|
||||
|
||||
# add any generic parameters
|
||||
for i in 1..genericParams:
|
||||
result.add n[givenRegularParams + i]
|
||||
for i in 1 .. genericParams:
|
||||
result.addSon n.sons[givenRegularParams + i]
|
||||
|
||||
# to prevent endless recursion in template instantiation
|
||||
const evalTemplateLimit* = 1000
|
||||
@@ -161,7 +155,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
|
||||
if x.kind in nkCallKinds:
|
||||
for i in 1..<x.len:
|
||||
if x[i].kind in nkCallKinds:
|
||||
x[i].info = info
|
||||
x.sons[i].info = info
|
||||
else:
|
||||
result = newNodeI(nkStmtListExpr, info)
|
||||
var d = newNodeI(nkComesFrom, info)
|
||||
@@ -172,8 +166,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
|
||||
|
||||
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
conf: ConfigRef;
|
||||
ic: IdentCache; instID: ref int;
|
||||
idgen: IdGenerator;
|
||||
ic: IdentCache;
|
||||
fromHlo=false): PNode =
|
||||
inc(conf.evalTemplateCounter)
|
||||
if conf.evalTemplateCounter > evalTemplateLimit:
|
||||
@@ -188,15 +181,13 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
ctx.config = conf
|
||||
ctx.ic = ic
|
||||
initIdTable(ctx.mapping)
|
||||
ctx.instID = instID[]
|
||||
ctx.idgen = idgen
|
||||
|
||||
let body = tmpl.ast[bodyPos]
|
||||
let body = tmpl.getBody
|
||||
#echo "instantion of ", renderTree(body, {renderIds})
|
||||
if isAtom(body):
|
||||
result = newNodeI(nkPar, body.info)
|
||||
evalTemplateAux(body, args, ctx, result)
|
||||
if result.len == 1: result = result[0]
|
||||
if result.len == 1: result = result.sons[0]
|
||||
else:
|
||||
localError(conf, result.info, "illformed AST: " &
|
||||
renderTree(result, {renderNoComments}))
|
||||
@@ -205,12 +196,10 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
ctx.instLines = sfCallsite in tmpl.flags
|
||||
if ctx.instLines:
|
||||
result.info = n.info
|
||||
for i in 0..<body.safeLen:
|
||||
evalTemplateAux(body[i], args, ctx, result)
|
||||
for i in 0 ..< safeLen(body):
|
||||
evalTemplateAux(body.sons[i], args, ctx, result)
|
||||
result.flags.incl nfFromTemplate
|
||||
result = wrapInComesFrom(n.info, tmpl, result)
|
||||
#if ctx.debugActive:
|
||||
# echo "instantion of ", renderTree(result, {renderIds})
|
||||
dec(conf.evalTemplateCounter)
|
||||
# The instID must be unique for every template instantiation, so we increment it here
|
||||
inc instID[]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ type
|
||||
info: TLineInfo
|
||||
indent, emitPar: int
|
||||
x: string # the current input line
|
||||
outp: PLLStream # the output will be parsed by parser
|
||||
outp: PLLStream # the output will be parsed by pnimsyn
|
||||
subsChar, nimDirective: char
|
||||
emit, conc, toStr: string
|
||||
curly, bracket, par: int
|
||||
@@ -75,7 +75,7 @@ proc parseLine(p: var TTmplParser) =
|
||||
let d = j
|
||||
var keyw = ""
|
||||
while j < len and p.x[j] in PatternChars:
|
||||
keyw.add(p.x[j])
|
||||
add(keyw, p.x[j])
|
||||
inc(j)
|
||||
|
||||
scanPar(p, j)
|
||||
@@ -199,8 +199,8 @@ proc parseLine(p: var TTmplParser) =
|
||||
inc(j)
|
||||
llStreamWrite(p.outp, "\\n\"")
|
||||
|
||||
proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
|
||||
call: PNode): PLLStream =
|
||||
proc filterTmpl*(stdin: PLLStream, filename: AbsoluteFile,
|
||||
call: PNode; conf: ConfigRef): PLLStream =
|
||||
var p: TTmplParser
|
||||
p.config = conf
|
||||
p.info = newLineInfo(conf, filename, 0, 0)
|
||||
@@ -214,9 +214,9 @@ proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
|
||||
p.x = newStringOfCap(120)
|
||||
# do not process the first line which contains the directive:
|
||||
if llStreamReadLine(p.inp, p.x):
|
||||
inc p.info.line
|
||||
p.info.line = p.info.line + 1'u16
|
||||
while llStreamReadLine(p.inp, p.x):
|
||||
inc p.info.line
|
||||
p.info.line = p.info.line + 1'u16
|
||||
parseLine(p)
|
||||
newLine(p)
|
||||
result = p.outp
|
||||
|
||||
@@ -20,13 +20,13 @@ proc invalidPragma(conf: ConfigRef; n: PNode) =
|
||||
proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
|
||||
result = nil
|
||||
if n.kind in {nkEmpty..nkNilLit}: return
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkExprEqExpr:
|
||||
if n[i][0].kind != nkIdent: invalidPragma(conf, n)
|
||||
if cmpIgnoreStyle(n[i][0].ident.s, name) == 0:
|
||||
return n[i][1]
|
||||
for i in 1 ..< len(n):
|
||||
if n.sons[i].kind == nkExprEqExpr:
|
||||
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(conf, n)
|
||||
if cmpIgnoreStyle(n.sons[i].sons[0].ident.s, name) == 0:
|
||||
return n.sons[i].sons[1]
|
||||
elif i == pos:
|
||||
return n[i]
|
||||
return n.sons[i]
|
||||
|
||||
proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
|
||||
var x = getArg(conf, n, name, pos)
|
||||
@@ -55,7 +55,7 @@ proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, cal
|
||||
var line = newStringOfCap(80)
|
||||
while llStreamReadLine(stdin, line):
|
||||
var stripped = strip(line, leading, trailing)
|
||||
if pattern.len == 0 or startsWith(stripped, pattern):
|
||||
if len(pattern) == 0 or startsWith(stripped, pattern):
|
||||
llStreamWriteln(result, stripped)
|
||||
else:
|
||||
llStreamWriteln(result, line)
|
||||
@@ -63,7 +63,7 @@ proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, cal
|
||||
|
||||
proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
|
||||
var sub = strArg(conf, call, "sub", 1, "")
|
||||
if sub.len == 0: invalidPragma(conf, call)
|
||||
if len(sub) == 0: invalidPragma(conf, call)
|
||||
var by = strArg(conf, call, "by", 2, "")
|
||||
result = llStreamOpen("")
|
||||
var line = newStringOfCap(80)
|
||||
|
||||
89
compiler/forloops.nim
Normal file
89
compiler/forloops.nim
Normal file
@@ -0,0 +1,89 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements for loop detection for better C code generation.
|
||||
|
||||
import ast, astalgo
|
||||
|
||||
const
|
||||
someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr}
|
||||
|
||||
proc isCounter(s: PSym): bool {.inline.} =
|
||||
s.kind in {skResult, skVar, skLet, skTemp} and
|
||||
{sfGlobal, sfAddrTaken} * s.flags == {}
|
||||
|
||||
proc isCall(n: PNode): bool {.inline.} =
|
||||
n.kind in nkCallKinds and n[0].kind == nkSym
|
||||
|
||||
proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
|
||||
|
||||
proc getCounter(lastStmt: PNode): PSym =
|
||||
if lastStmt.isCall:
|
||||
let op = lastStmt.sym
|
||||
if op.magic in {mDec, mInc} or
|
||||
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
|
||||
if op[1].kind == nkSym and isCounter(op[1].sym):
|
||||
result = op[1].sym
|
||||
|
||||
proc counterInTree(n, loop: PNode; counter: PSym): bool =
|
||||
# prune the search tree: within the loop the counter may be used:
|
||||
if n == loop: return
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym == counter: return true
|
||||
of nkVarSection, nkLetSection:
|
||||
# definitions are fine!
|
||||
for it in n:
|
||||
if counterInTree(it.lastSon): return true
|
||||
else:
|
||||
for i in 0 ..< safeLen(n):
|
||||
if counterInTree(n[i], loop, counter): return true
|
||||
|
||||
proc copyExcept(n: PNode, x, dest: PNode) =
|
||||
if x == n: return
|
||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
for i in 0 ..< n.len: copyExcept(n[i], x, dest)
|
||||
else:
|
||||
dest.add n
|
||||
|
||||
type
|
||||
ForLoop* = object
|
||||
counter*: PSym
|
||||
init*, cond*, increment*, body*: PNode
|
||||
|
||||
proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||
## returns 'counter == nil' if the while loop 'n' is not a for loop:
|
||||
assert loop.kind == nkWhileStmt
|
||||
let cond == loop[0]
|
||||
|
||||
if not cond.isCall: return
|
||||
if cond[0].sym.magic notin someCmp: return
|
||||
|
||||
var lastStmt = loop[1]
|
||||
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
|
||||
lastStmt = lastStmt.lastSon
|
||||
|
||||
let counter = getCounter(lastStmt)
|
||||
if counter.isNil or counter.ast.isNil: return
|
||||
|
||||
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
|
||||
|
||||
if cond[1] =~ counter or cond[2] =~ counter:
|
||||
# ok, now check 'counter' is not used *after* the loop
|
||||
if counterInTree(fullTree, loop, counter): return
|
||||
# ok, success, fill in the fields:
|
||||
result.counter = counter
|
||||
result.init = counter.ast
|
||||
result.cond = cond
|
||||
result.increment = lastStmt
|
||||
result.body = newNodeI(nkStmtList, loop[1].info)
|
||||
copyExcept(loop[1], lastStmt, result.body)
|
||||
@@ -14,17 +14,18 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
||||
|
||||
const
|
||||
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqStr, mEqSet, mEqCString}
|
||||
mEqUntracedRef, mEqStr, mEqSet, mEqCString}
|
||||
|
||||
# set excluded here as the semantics are vastly different:
|
||||
someLe = {mLeI, mLeF64, mLeU, mLeEnum,
|
||||
someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
mLeCh, mLeB, mLePtr, mLeStr}
|
||||
someLt = {mLtI, mLtF64, mLtU, mLtEnum,
|
||||
someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr, mLtStr}
|
||||
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
|
||||
mXLenStr, mXLenSeq}
|
||||
|
||||
someIn = {mInSet}
|
||||
someIn = {mInRange, mInSet}
|
||||
|
||||
someHigh = {mHigh}
|
||||
# we don't list unsigned here because wrap around semantics suck for
|
||||
@@ -46,7 +47,7 @@ proc isLet(n: PNode): bool =
|
||||
if n.sym.kind in {skLet, skTemp, skForVar}:
|
||||
result = true
|
||||
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
|
||||
abstractInst).kind notin {tyVar}:
|
||||
abstractInst).kind != tyVar:
|
||||
result = true
|
||||
|
||||
proc isVar(n: PNode): bool =
|
||||
@@ -64,16 +65,16 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||
while true:
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
|
||||
n = n[0]
|
||||
n = n.sons[0]
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
n = n[0]
|
||||
n = n.sons[0]
|
||||
inc derefs
|
||||
of nkBracketExpr:
|
||||
if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
|
||||
n = n[0]
|
||||
if isConstExpr(n.sons[1]) or isLet(n.sons[1]) or isConstExpr(n.sons[1].skipConv):
|
||||
n = n.sons[0]
|
||||
else: return
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
n = n[1]
|
||||
n = n.sons[1]
|
||||
else:
|
||||
break
|
||||
result = n.isLet and derefs <= ord(isApprox)
|
||||
@@ -82,36 +83,56 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||
|
||||
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
||||
|
||||
type
|
||||
Operators* = object
|
||||
opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym
|
||||
opAdd, opSub, opMul, opDiv, opLen: PSym
|
||||
|
||||
proc initOperators*(g: ModuleGraph): Operators =
|
||||
result.opLe = createMagic(g, "<=", mLeI)
|
||||
result.opLt = createMagic(g, "<", mLtI)
|
||||
result.opAnd = createMagic(g, "and", mAnd)
|
||||
result.opOr = createMagic(g, "or", mOr)
|
||||
result.opIsNil = createMagic(g, "isnil", mIsNil)
|
||||
result.opEq = createMagic(g, "==", mEqI)
|
||||
result.opAdd = createMagic(g, "+", mAddI)
|
||||
result.opSub = createMagic(g, "-", mSubI)
|
||||
result.opMul = createMagic(g, "*", mMulI)
|
||||
result.opDiv = createMagic(g, "div", mDivI)
|
||||
result.opLen = createMagic(g, "len", mLengthSeq)
|
||||
result.opNot = createMagic(g, "not", mNot)
|
||||
result.opContains = createMagic(g, "contains", mInSet)
|
||||
|
||||
proc swapArgs(fact: PNode, newOp: PSym): PNode =
|
||||
result = newNodeI(nkCall, fact.info, 3)
|
||||
result[0] = newSymNode(newOp)
|
||||
result[1] = fact[2]
|
||||
result[2] = fact[1]
|
||||
result.sons[0] = newSymNode(newOp)
|
||||
result.sons[1] = fact.sons[2]
|
||||
result.sons[2] = fact.sons[1]
|
||||
|
||||
proc neg(n: PNode; o: Operators): PNode =
|
||||
if n == nil: return nil
|
||||
case n.getMagic
|
||||
of mNot:
|
||||
result = n[1]
|
||||
result = n.sons[1]
|
||||
of someLt:
|
||||
# not (a < b) == a >= b == b <= a
|
||||
result = swapArgs(n, o.opLe)
|
||||
of someLe:
|
||||
result = swapArgs(n, o.opLt)
|
||||
of mInSet:
|
||||
if n[1].kind != nkCurly: return nil
|
||||
let t = n[2].typ.skipTypes(abstractInst)
|
||||
if n.sons[1].kind != nkCurly: return nil
|
||||
let t = n.sons[2].typ.skipTypes(abstractInst)
|
||||
result = newNodeI(nkCall, n.info, 3)
|
||||
result[0] = n[0]
|
||||
result[2] = n[2]
|
||||
result.sons[0] = n.sons[0]
|
||||
result.sons[2] = n.sons[2]
|
||||
if t.kind == tyEnum:
|
||||
var s = newNodeIT(nkCurly, n.info, n[1].typ)
|
||||
var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
|
||||
for e in t.n:
|
||||
let eAsNode = newIntNode(nkIntLit, e.sym.position)
|
||||
if not inSet(n[1], eAsNode): s.add eAsNode
|
||||
result[1] = s
|
||||
if not inSet(n.sons[1], eAsNode): s.add eAsNode
|
||||
result.sons[1] = s
|
||||
#elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
|
||||
# result[1] = complement(n[1])
|
||||
# result.sons[1] = complement(n.sons[1])
|
||||
else:
|
||||
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
|
||||
# XXX todo
|
||||
@@ -119,13 +140,13 @@ proc neg(n: PNode; o: Operators): PNode =
|
||||
of mOr:
|
||||
# not (a or b) --> not a and not b
|
||||
let
|
||||
a = n[1].neg(o)
|
||||
b = n[2].neg(o)
|
||||
a = n.sons[1].neg(o)
|
||||
b = n.sons[2].neg(o)
|
||||
if a != nil and b != nil:
|
||||
result = newNodeI(nkCall, n.info, 3)
|
||||
result[0] = newSymNode(o.opAnd)
|
||||
result[1] = a
|
||||
result[2] = b
|
||||
result.sons[0] = newSymNode(o.opAnd)
|
||||
result.sons[1] = a
|
||||
result.sons[2] = b
|
||||
elif a != nil:
|
||||
result = a
|
||||
elif b != nil:
|
||||
@@ -133,19 +154,19 @@ proc neg(n: PNode; o: Operators): PNode =
|
||||
else:
|
||||
# leave not (a == 4) as it is
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result[0] = newSymNode(o.opNot)
|
||||
result[1] = n
|
||||
result.sons[0] = newSymNode(o.opNot)
|
||||
result.sons[1] = n
|
||||
|
||||
proc buildCall*(op: PSym; a: PNode): PNode =
|
||||
proc buildCall(op: PSym; a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info, 2)
|
||||
result[0] = newSymNode(op)
|
||||
result[1] = a
|
||||
result.sons[0] = newSymNode(op)
|
||||
result.sons[1] = a
|
||||
|
||||
proc buildCall*(op: PSym; a, b: PNode): PNode =
|
||||
proc buildCall(op: PSym; a, b: PNode): PNode =
|
||||
result = newNodeI(nkInfix, a.info, 3)
|
||||
result[0] = newSymNode(op)
|
||||
result[1] = a
|
||||
result[2] = b
|
||||
result.sons[0] = newSymNode(op)
|
||||
result.sons[1] = a
|
||||
result.sons[2] = b
|
||||
|
||||
proc `|+|`(a, b: PNode): PNode =
|
||||
result = copyNode(a)
|
||||
@@ -229,35 +250,35 @@ proc pred(n: PNode): PNode =
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc buildLe*(o: Operators; a, b: PNode): PNode =
|
||||
result = o.opLe.buildCall(a, b)
|
||||
|
||||
proc canon*(n: PNode; o: Operators): PNode =
|
||||
# XXX for now only the new code in 'semparallel' uses this
|
||||
if n.safeLen >= 1:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = canon(n[i], o)
|
||||
for i in 0 ..< n.len:
|
||||
result.sons[i] = canon(n.sons[i], o)
|
||||
elif n.kind == nkSym and n.sym.kind == skLet and
|
||||
n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin +
|
||||
someMax + someHigh + someSub + someLen + someDiv):
|
||||
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
|
||||
result = n.sym.astdef.copyTree
|
||||
else:
|
||||
result = n
|
||||
case result.getMagic
|
||||
of someEq, someAdd, someMul, someMin, someMax:
|
||||
# these are symmetric; put value as last:
|
||||
if result[1].isValue and not result[2].isValue:
|
||||
result = swapArgs(result, result[0].sym)
|
||||
if result.sons[1].isValue and not result.sons[2].isValue:
|
||||
result = swapArgs(result, result.sons[0].sym)
|
||||
# (4 + foo) + 2 --> (foo + 4) + 2
|
||||
of someHigh:
|
||||
# high == len+(-1)
|
||||
result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
|
||||
of mUnaryLt:
|
||||
result = buildCall(o.opAdd, result[1], minusOne())
|
||||
of someSub:
|
||||
# x - 4 --> x + (-4)
|
||||
result = negate(result[1], result[2], result, o)
|
||||
of someLen:
|
||||
result[0] = o.opLen.newSymNode
|
||||
of someLt - {mLtF64}:
|
||||
result.sons[0] = o.opLen.newSymNode
|
||||
of someLt:
|
||||
# x < y same as x <= y-1:
|
||||
let y = n[2].canon(o)
|
||||
let p = pred(y)
|
||||
@@ -297,16 +318,16 @@ proc canon*(n: PNode; o: Operators): PNode =
|
||||
if plus != nil and not isLetLocation(x, true):
|
||||
result = buildCall(result[0].sym, plus, y[1])
|
||||
else: discard
|
||||
elif x.isValue and y.getMagic in someAdd and y[2].kind == x.kind:
|
||||
elif x.isValue and y.getMagic in someAdd and y[2].isValue:
|
||||
# 0 <= a.len + 3
|
||||
# -3 <= a.len
|
||||
result[1] = x |-| y[2]
|
||||
result[2] = y[1]
|
||||
elif x.isValue and y.getMagic in someSub and y[2].kind == x.kind:
|
||||
result.sons[1] = x |-| y[2]
|
||||
result.sons[2] = y[1]
|
||||
elif x.isValue and y.getMagic in someSub and y[2].isValue:
|
||||
# 0 <= a.len - 3
|
||||
# 3 <= a.len
|
||||
result[1] = x |+| y[2]
|
||||
result[2] = y[1]
|
||||
result.sons[1] = x |+| y[2]
|
||||
result.sons[2] = y[1]
|
||||
else: discard
|
||||
|
||||
proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
|
||||
@@ -315,43 +336,41 @@ proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
|
||||
proc usefulFact(n: PNode; o: Operators): PNode =
|
||||
case n.getMagic
|
||||
of someEq:
|
||||
if skipConv(n[2]).kind == nkNilLit and (
|
||||
isLetLocation(n[1], false) or isVar(n[1])):
|
||||
result = o.opIsNil.buildCall(n[1])
|
||||
if skipConv(n.sons[2]).kind == nkNilLit and (
|
||||
isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
|
||||
result = o.opIsNil.buildCall(n.sons[1])
|
||||
else:
|
||||
if isLetLocation(n[1], true) or isLetLocation(n[2], true):
|
||||
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
|
||||
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
|
||||
result = n
|
||||
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
|
||||
result = n
|
||||
of someLe+someLt:
|
||||
if isLetLocation(n[1], true) or isLetLocation(n[2], true):
|
||||
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
|
||||
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
|
||||
result = n
|
||||
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
|
||||
# XXX Rethink this whole idea of 'usefulFact' for semparallel
|
||||
result = n
|
||||
of mIsNil:
|
||||
if isLetLocation(n[1], false) or isVar(n[1]):
|
||||
if isLetLocation(n.sons[1], false) or isVar(n.sons[1]):
|
||||
result = n
|
||||
of someIn:
|
||||
if isLetLocation(n[1], true):
|
||||
if isLetLocation(n.sons[1], true):
|
||||
result = n
|
||||
of mAnd:
|
||||
let
|
||||
a = usefulFact(n[1], o)
|
||||
b = usefulFact(n[2], o)
|
||||
a = usefulFact(n.sons[1], o)
|
||||
b = usefulFact(n.sons[2], o)
|
||||
if a != nil and b != nil:
|
||||
result = newNodeI(nkCall, n.info, 3)
|
||||
result[0] = newSymNode(o.opAnd)
|
||||
result[1] = a
|
||||
result[2] = b
|
||||
result.sons[0] = newSymNode(o.opAnd)
|
||||
result.sons[1] = a
|
||||
result.sons[2] = b
|
||||
elif a != nil:
|
||||
result = a
|
||||
elif b != nil:
|
||||
result = b
|
||||
of mNot:
|
||||
let a = usefulFact(n[1], o)
|
||||
let a = usefulFact(n.sons[1], o)
|
||||
if a != nil:
|
||||
result = a.neg(o)
|
||||
of mOr:
|
||||
@@ -362,13 +381,13 @@ proc usefulFact(n: PNode; o: Operators): PNode =
|
||||
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
|
||||
# not (x != 3 and y != 2)
|
||||
let
|
||||
a = usefulFact(n[1], o).neg(o)
|
||||
b = usefulFact(n[2], o).neg(o)
|
||||
a = usefulFact(n.sons[1], o).neg(o)
|
||||
b = usefulFact(n.sons[2], o).neg(o)
|
||||
if a != nil and b != nil:
|
||||
result = newNodeI(nkCall, n.info, 3)
|
||||
result[0] = newSymNode(o.opAnd)
|
||||
result[1] = a
|
||||
result[2] = b
|
||||
result.sons[0] = newSymNode(o.opAnd)
|
||||
result.sons[1] = a
|
||||
result.sons[2] = b
|
||||
result = result.neg(o)
|
||||
elif n.kind == nkSym and n.sym.kind == skLet:
|
||||
# consider:
|
||||
@@ -384,24 +403,14 @@ proc usefulFact(n: PNode; o: Operators): PNode =
|
||||
type
|
||||
TModel* = object
|
||||
s*: seq[PNode] # the "knowledge base"
|
||||
g*: ModuleGraph
|
||||
beSmart*: bool
|
||||
o*: Operators
|
||||
|
||||
proc addFact*(m: var TModel, nn: PNode) =
|
||||
let n = usefulFact(nn, m.g.operators)
|
||||
if n != nil:
|
||||
if not m.beSmart:
|
||||
m.s.add n
|
||||
else:
|
||||
let c = canon(n, m.g.operators)
|
||||
if c.getMagic == mAnd:
|
||||
addFact(m, c[1])
|
||||
addFact(m, c[2])
|
||||
else:
|
||||
m.s.add c
|
||||
let n = usefulFact(nn, m.o)
|
||||
if n != nil: m.s.add n
|
||||
|
||||
proc addFactNeg*(m: var TModel, n: PNode) =
|
||||
let n = n.neg(m.g.operators)
|
||||
let n = n.neg(m.o)
|
||||
if n != nil: addFact(m, n)
|
||||
|
||||
proc sameOpr(a, b: PSym): bool =
|
||||
@@ -433,24 +442,18 @@ proc sameTree*(a, b: PNode): bool =
|
||||
of nkType: result = a.typ == b.typ
|
||||
of nkEmpty, nkNilLit: result = true
|
||||
else:
|
||||
if a.len == b.len:
|
||||
for i in 0..<a.len:
|
||||
if not sameTree(a[i], b[i]): return
|
||||
if len(a) == len(b):
|
||||
for i in 0 ..< len(a):
|
||||
if not sameTree(a.sons[i], b.sons[i]): return
|
||||
result = true
|
||||
|
||||
proc hasSubTree(n, x: PNode): bool =
|
||||
if n.sameTree(x): result = true
|
||||
else:
|
||||
case n.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
result = n.sameTree(x)
|
||||
of nkFormalParams:
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
if hasSubTree(n[i], x): return true
|
||||
for i in 0..safeLen(n)-1:
|
||||
if hasSubTree(n.sons[i], x): return true
|
||||
|
||||
proc invalidateFacts*(s: var seq[PNode], n: PNode) =
|
||||
proc invalidateFacts*(m: var TModel, n: PNode) =
|
||||
# We are able to guard local vars (as opposed to 'let' variables)!
|
||||
# 'while p != nil: f(p); p = p.next'
|
||||
# This is actually quite easy to do:
|
||||
@@ -468,32 +471,29 @@ proc invalidateFacts*(s: var seq[PNode], n: PNode) =
|
||||
# The same mechanism could be used for more complex data stored on the heap;
|
||||
# procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
|
||||
# could CSE these expressions then and help C's optimizer.
|
||||
for i in 0..high(s):
|
||||
if s[i] != nil and s[i].hasSubTree(n): s[i] = nil
|
||||
|
||||
proc invalidateFacts*(m: var TModel, n: PNode) =
|
||||
invalidateFacts(m.s, n)
|
||||
for i in 0..high(m.s):
|
||||
if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil
|
||||
|
||||
proc valuesUnequal(a, b: PNode): bool =
|
||||
if a.isValue and b.isValue:
|
||||
result = not sameValue(a, b)
|
||||
|
||||
proc impliesEq(fact, eq: PNode): TImplication =
|
||||
let (loc, val) = if isLocation(eq[1]): (1, 2) else: (2, 1)
|
||||
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
|
||||
|
||||
case fact[0].sym.magic
|
||||
case fact.sons[0].sym.magic
|
||||
of someEq:
|
||||
if sameTree(fact[1], eq[loc]):
|
||||
if sameTree(fact.sons[1], eq.sons[loc]):
|
||||
# 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
|
||||
elif sameTree(fact[2], eq[loc]):
|
||||
if sameTree(fact[1], eq[val]): result = impYes
|
||||
elif valuesUnequal(fact[1], eq[val]): result = impNo
|
||||
if sameTree(fact.sons[2], eq.sons[val]): result = impYes
|
||||
elif valuesUnequal(fact.sons[2], eq.sons[val]): result = impNo
|
||||
elif sameTree(fact.sons[2], eq.sons[loc]):
|
||||
if sameTree(fact.sons[1], eq.sons[val]): result = impYes
|
||||
elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo
|
||||
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
|
||||
if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
|
||||
if inSet(fact.sons[1], eq.sons[val]): result = impYes
|
||||
else: result = impNo
|
||||
of mNot, mOr, mAnd: assert(false, "impliesEq")
|
||||
else: discard
|
||||
@@ -536,28 +536,28 @@ proc compareSets(a, b: PNode): TImplication =
|
||||
elif intersectSets(nil, a, b).len == 0: result = impNo
|
||||
|
||||
proc impliesIn(fact, loc, aSet: PNode): TImplication =
|
||||
case fact[0].sym.magic
|
||||
case fact.sons[0].sym.magic
|
||||
of someEq:
|
||||
if sameTree(fact[1], loc):
|
||||
if inSet(aSet, fact[2]): result = impYes
|
||||
if sameTree(fact.sons[1], loc):
|
||||
if inSet(aSet, fact.sons[2]): result = impYes
|
||||
else: result = impNo
|
||||
elif sameTree(fact[2], loc):
|
||||
if inSet(aSet, fact[1]): result = impYes
|
||||
elif sameTree(fact.sons[2], loc):
|
||||
if inSet(aSet, fact.sons[1]): result = impYes
|
||||
else: result = impNo
|
||||
of mInSet:
|
||||
if sameTree(fact[2], loc):
|
||||
result = compareSets(fact[1], aSet)
|
||||
if sameTree(fact.sons[2], loc):
|
||||
result = compareSets(fact.sons[1], aSet)
|
||||
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)
|
||||
if sameTree(fact.sons[1], loc):
|
||||
result = leImpliesIn(fact.sons[1], fact.sons[2], aSet)
|
||||
elif sameTree(fact.sons[2], loc):
|
||||
result = geImpliesIn(fact.sons[2], fact.sons[1], aSet)
|
||||
of someLt:
|
||||
if sameTree(fact[1], loc):
|
||||
result = leImpliesIn(fact[1], fact[2].pred, aSet)
|
||||
elif sameTree(fact[2], loc):
|
||||
if sameTree(fact.sons[1], loc):
|
||||
result = leImpliesIn(fact.sons[1], fact.sons[2].pred, aSet)
|
||||
elif sameTree(fact.sons[2], loc):
|
||||
# 4 < x --> 3 <= x
|
||||
result = geImpliesIn(fact[2], fact[1].pred, aSet)
|
||||
result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
|
||||
of mNot, mOr, mAnd: assert(false, "impliesIn")
|
||||
else: discard
|
||||
|
||||
@@ -567,93 +567,90 @@ proc valueIsNil(n: PNode): TImplication =
|
||||
else: impUnknown
|
||||
|
||||
proc impliesIsNil(fact, eq: PNode): TImplication =
|
||||
case fact[0].sym.magic
|
||||
case fact.sons[0].sym.magic
|
||||
of mIsNil:
|
||||
if sameTree(fact[1], eq[1]):
|
||||
if sameTree(fact.sons[1], eq.sons[1]):
|
||||
result = impYes
|
||||
of someEq:
|
||||
if sameTree(fact[1], eq[1]):
|
||||
result = valueIsNil(fact[2].skipConv)
|
||||
elif sameTree(fact[2], eq[1]):
|
||||
result = valueIsNil(fact[1].skipConv)
|
||||
if sameTree(fact.sons[1], eq.sons[1]):
|
||||
result = valueIsNil(fact.sons[2].skipConv)
|
||||
elif sameTree(fact.sons[2], eq.sons[1]):
|
||||
result = valueIsNil(fact.sons[1].skipConv)
|
||||
of mNot, mOr, mAnd: assert(false, "impliesIsNil")
|
||||
else: discard
|
||||
|
||||
proc impliesGe(fact, x, c: PNode): TImplication =
|
||||
assert isLocation(x)
|
||||
case fact[0].sym.magic
|
||||
case fact.sons[0].sym.magic
|
||||
of someEq:
|
||||
if sameTree(fact[1], x):
|
||||
if isValue(fact[2]) and isValue(c):
|
||||
if sameTree(fact.sons[1], x):
|
||||
if isValue(fact.sons[2]) and isValue(c):
|
||||
# fact: x = 4; question x >= 56? --> true iff 4 >= 56
|
||||
if leValue(c, fact[2]): result = impYes
|
||||
if leValue(c, fact.sons[2]): result = impYes
|
||||
else: result = impNo
|
||||
elif sameTree(fact[2], x):
|
||||
if isValue(fact[1]) and isValue(c):
|
||||
if leValue(c, fact[1]): result = impYes
|
||||
elif sameTree(fact.sons[2], x):
|
||||
if isValue(fact.sons[1]) and isValue(c):
|
||||
if leValue(c, fact.sons[1]): result = impYes
|
||||
else: result = impNo
|
||||
of someLt:
|
||||
if sameTree(fact[1], x):
|
||||
if isValue(fact[2]) and isValue(c):
|
||||
if sameTree(fact.sons[1], x):
|
||||
if isValue(fact.sons[2]) and isValue(c):
|
||||
# fact: x < 4; question N <= x? --> false iff N <= 4
|
||||
if leValue(fact[2], c): result = impNo
|
||||
if leValue(fact.sons[2], c): result = impNo
|
||||
# fact: x < 4; question 2 <= x? --> we don't know
|
||||
elif sameTree(fact[2], x):
|
||||
elif sameTree(fact.sons[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
|
||||
if isValue(fact.sons[1]) and isValue(c):
|
||||
if leValue(c.pred, fact.sons[1]): result = impYes
|
||||
of someLe:
|
||||
if sameTree(fact[1], x):
|
||||
if isValue(fact[2]) and isValue(c):
|
||||
if sameTree(fact.sons[1], x):
|
||||
if isValue(fact.sons[2]) and isValue(c):
|
||||
# fact: x <= 4; question x >= 56? --> false iff 4 <= 56
|
||||
if leValue(fact[2], c): result = impNo
|
||||
if leValue(fact.sons[2], c): result = impNo
|
||||
# fact: x <= 4; question x >= 2? --> we don't know
|
||||
elif sameTree(fact[2], x):
|
||||
elif sameTree(fact.sons[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
|
||||
if isValue(fact.sons[1]) and isValue(c):
|
||||
if leValue(c, fact.sons[1]): result = impYes
|
||||
of mNot, mOr, mAnd: assert(false, "impliesGe")
|
||||
else: discard
|
||||
|
||||
proc impliesLe(fact, x, c: PNode): TImplication =
|
||||
if not isLocation(x):
|
||||
if c.isValue:
|
||||
if leValue(x, x): return impYes
|
||||
else: return impNo
|
||||
return impliesGe(fact, c, x)
|
||||
case fact[0].sym.magic
|
||||
case fact.sons[0].sym.magic
|
||||
of someEq:
|
||||
if sameTree(fact[1], x):
|
||||
if isValue(fact[2]) and isValue(c):
|
||||
if sameTree(fact.sons[1], x):
|
||||
if isValue(fact.sons[2]) and isValue(c):
|
||||
# fact: x = 4; question x <= 56? --> true iff 4 <= 56
|
||||
if leValue(fact[2], c): result = impYes
|
||||
if leValue(fact.sons[2], c): result = impYes
|
||||
else: result = impNo
|
||||
elif sameTree(fact[2], x):
|
||||
if isValue(fact[1]) and isValue(c):
|
||||
if leValue(fact[1], c): result = impYes
|
||||
elif sameTree(fact.sons[2], x):
|
||||
if isValue(fact.sons[1]) and isValue(c):
|
||||
if leValue(fact.sons[1], c): result = impYes
|
||||
else: result = impNo
|
||||
of someLt:
|
||||
if sameTree(fact[1], x):
|
||||
if isValue(fact[2]) and isValue(c):
|
||||
if sameTree(fact.sons[1], x):
|
||||
if isValue(fact.sons[2]) and isValue(c):
|
||||
# fact: x < 4; question x <= N? --> true iff N-1 <= 4
|
||||
if leValue(fact[2], c.pred): result = impYes
|
||||
if leValue(fact.sons[2], c.pred): result = impYes
|
||||
# fact: x < 4; question x <= 2? --> we don't know
|
||||
elif sameTree(fact[2], x):
|
||||
elif sameTree(fact.sons[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
|
||||
if isValue(fact.sons[1]) and isValue(c):
|
||||
if leValue(c, fact.sons[1]): result = impNo
|
||||
|
||||
of someLe:
|
||||
if sameTree(fact[1], x):
|
||||
if isValue(fact[2]) and isValue(c):
|
||||
if sameTree(fact.sons[1], x):
|
||||
if isValue(fact.sons[2]) and isValue(c):
|
||||
# fact: x <= 4; question x <= 56? --> true iff 4 <= 56
|
||||
if leValue(fact[2], c): result = impYes
|
||||
if leValue(fact.sons[2], c): result = impYes
|
||||
# fact: x <= 4; question x <= 2? --> we don't know
|
||||
|
||||
elif sameTree(fact[2], x):
|
||||
elif sameTree(fact.sons[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
|
||||
if isValue(fact.sons[1]) and isValue(c):
|
||||
if leValue(c, fact.sons[1].pred): result = impNo
|
||||
|
||||
of mNot, mOr, mAnd: assert(false, "impliesLe")
|
||||
else: discard
|
||||
@@ -689,32 +686,32 @@ proc factImplies(fact, prop: PNode): TImplication =
|
||||
|
||||
# (not a) -> b compute as not (a -> b) ???
|
||||
# == not a or not b == not (a and b)
|
||||
let arg = fact[1]
|
||||
let arg = fact.sons[1]
|
||||
case arg.getMagic
|
||||
of mIsNil, mEqRef:
|
||||
return ~factImplies(arg, prop)
|
||||
of mAnd:
|
||||
# not (a and b) means not a or not b:
|
||||
# a or b --> both need to imply 'prop'
|
||||
let a = factImplies(arg[1], prop)
|
||||
let b = factImplies(arg[2], prop)
|
||||
let a = factImplies(arg.sons[1], prop)
|
||||
let b = factImplies(arg.sons[2], prop)
|
||||
if a == b: return ~a
|
||||
return impUnknown
|
||||
else:
|
||||
return impUnknown
|
||||
of mAnd:
|
||||
result = factImplies(fact[1], prop)
|
||||
result = factImplies(fact.sons[1], prop)
|
||||
if result != impUnknown: return result
|
||||
return factImplies(fact[2], prop)
|
||||
return factImplies(fact.sons[2], prop)
|
||||
else: discard
|
||||
|
||||
case prop[0].sym.magic
|
||||
of mNot: result = ~fact.factImplies(prop[1])
|
||||
case prop.sons[0].sym.magic
|
||||
of mNot: result = ~fact.factImplies(prop.sons[1])
|
||||
of mIsNil: result = impliesIsNil(fact, prop)
|
||||
of someEq: result = impliesEq(fact, prop)
|
||||
of someLe: result = impliesLe(fact, prop[1], prop[2])
|
||||
of someLt: result = impliesLt(fact, prop[1], prop[2])
|
||||
of mInSet: result = impliesIn(fact, prop[2], prop[1])
|
||||
of someLe: result = impliesLe(fact, prop.sons[1], prop.sons[2])
|
||||
of someLt: result = impliesLt(fact, prop.sons[1], prop.sons[2])
|
||||
of mInSet: result = impliesIn(fact, prop.sons[2], prop.sons[1])
|
||||
else: result = impUnknown
|
||||
|
||||
proc doesImply*(facts: TModel, prop: PNode): TImplication =
|
||||
@@ -726,7 +723,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication =
|
||||
if result != impUnknown: return
|
||||
|
||||
proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
|
||||
result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators))
|
||||
result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
|
||||
|
||||
proc simpleSlice*(a, b: PNode): BiggestInt =
|
||||
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
|
||||
@@ -780,7 +777,12 @@ macro `=~`(x: PNode, pat: untyped): bool =
|
||||
|
||||
var conds = newTree(nnkBracket)
|
||||
m(x, pat, conds)
|
||||
result = nestList(ident"and", conds)
|
||||
when compiles(nestList(ident"and", conds)):
|
||||
result = nestList(ident"and", conds)
|
||||
#elif declared(macros.toNimIdent):
|
||||
# result = nestList(toNimIdent"and", conds)
|
||||
else:
|
||||
result = nestList(!"and", conds)
|
||||
|
||||
proc isMinusOne(n: PNode): bool =
|
||||
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
|
||||
@@ -800,7 +802,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||
if lastOrd(nil, a.typ) <= b.intVal: return impYes
|
||||
# 3 <= x iff low(x) <= 3
|
||||
if a.isValue and b.typ != nil and b.typ.isOrdinalType:
|
||||
if a.intVal <= firstOrd(nil, b.typ): return impYes
|
||||
if firstOrd(nil, b.typ) <= a.intVal: return impYes
|
||||
|
||||
# x <= x
|
||||
if sameTree(a, b): return impYes
|
||||
@@ -811,11 +813,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||
|
||||
# x <= y+c if 0 <= c and x <= y
|
||||
# x <= y+(-c) if c <= 0 and y >= x
|
||||
if b.getMagic in someAdd:
|
||||
if zero() <=? b[2] and a <=? b[1]: return impYes
|
||||
# x <= y-c if x+c <= y
|
||||
if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]):
|
||||
return impYes
|
||||
if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
|
||||
|
||||
# x+c <= y if c <= 0 and x <= y
|
||||
if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes
|
||||
@@ -828,20 +826,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
|
||||
a[1][2].isValue:
|
||||
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
|
||||
if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
|
||||
if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
|
||||
return impYes
|
||||
|
||||
# x*3 + x == x*4. It follows that:
|
||||
# x*3 + y <= x*4 if y <= x and 3 <= 4
|
||||
if a =~ x*dc + y and b =~ x2*ec:
|
||||
if sameTree(x, x2):
|
||||
let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
|
||||
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
|
||||
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
|
||||
return impYes
|
||||
elif a =~ x*dc and b =~ x2*ec + y:
|
||||
#echo "BUG cam ehrer e ", a, " <=? ", b
|
||||
if sameTree(x, x2):
|
||||
let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
|
||||
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
|
||||
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
|
||||
return impYes
|
||||
|
||||
@@ -883,8 +881,8 @@ proc replaceSubTree(n, x, by: PNode): PNode =
|
||||
result = by
|
||||
elif hasSubTree(n, x):
|
||||
result = shallowCopy(n)
|
||||
for i in 0..n.safeLen-1:
|
||||
result[i] = replaceSubTree(n[i], x, by)
|
||||
for i in 0 .. safeLen(n)-1:
|
||||
result.sons[i] = replaceSubTree(n.sons[i], x, by)
|
||||
else:
|
||||
result = n
|
||||
|
||||
@@ -904,13 +902,10 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
|
||||
# --> true if (len-100) <= (len-1)
|
||||
let x = fact[1]
|
||||
let y = fact[2]
|
||||
# x <= y.
|
||||
# Question: x <= b? True iff y <= b.
|
||||
if sameTree(x, a):
|
||||
if ple(m, y, b) == impYes: return impYes
|
||||
if y.getMagic in someAdd and b.getMagic in someAdd and sameTree(y[1], b[1]):
|
||||
if ple(m, b[2], y[2]) == impYes:
|
||||
return impYes
|
||||
if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and
|
||||
sameTree(y[1], b[1]):
|
||||
if ple(m, b[2], y[2]) == impYes:
|
||||
return impYes
|
||||
|
||||
# x <= y implies a <= b if a <= x and y <= b
|
||||
if ple(m, a, x) == impYes:
|
||||
@@ -944,12 +939,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||
var b = bb
|
||||
if replacements.len > 0:
|
||||
m.s = @[]
|
||||
m.g = model.g
|
||||
m.o = model.o
|
||||
# make the other facts consistent:
|
||||
for fact in model.s:
|
||||
if fact != nil and fact.getMagic notin someEq:
|
||||
# XXX 'canon' should not be necessary here, but it is
|
||||
m.s.add applyReplacements(fact, replacements).canon(m.g.operators)
|
||||
m.s.add applyReplacements(fact, replacements).canon(m.o)
|
||||
a = applyReplacements(aa, replacements)
|
||||
b = applyReplacements(bb, replacements)
|
||||
else:
|
||||
@@ -958,60 +953,55 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||
result = pleViaModelRec(m, a, b)
|
||||
|
||||
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
||||
let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
|
||||
let x = canon(m.o.opLe.buildCall(a, b), m.o)
|
||||
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
|
||||
result = ple(m, x[1], x[2])
|
||||
if result == impUnknown:
|
||||
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
||||
let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators)
|
||||
let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
|
||||
result = ~ple(m, y[1], y[2])
|
||||
|
||||
proc addFactLe*(m: var TModel; a, b: PNode) =
|
||||
m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
|
||||
|
||||
proc addFactLt*(m: var TModel; a, b: PNode) =
|
||||
let bb = m.g.operators.opAdd.buildCall(b, minusOne())
|
||||
addFactLe(m, a, bb)
|
||||
m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
|
||||
|
||||
proc settype(n: PNode): PType =
|
||||
result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
|
||||
var idgen: IdGenerator
|
||||
addSonSkipIntLit(result, n.typ, idgen)
|
||||
result = newType(tySet, n.typ.owner)
|
||||
addSonSkipIntLit(result, n.typ)
|
||||
|
||||
proc buildOf(it, loc: PNode; o: Operators): PNode =
|
||||
var s = newNodeI(nkCurly, it.info, it.len-1)
|
||||
s.typ = settype(loc)
|
||||
for i in 0..<it.len-1: s[i] = it[i]
|
||||
for i in 0..it.len-2: s.sons[i] = it.sons[i]
|
||||
result = newNodeI(nkCall, it.info, 3)
|
||||
result[0] = newSymNode(o.opContains)
|
||||
result[1] = s
|
||||
result[2] = loc
|
||||
result.sons[0] = newSymNode(o.opContains)
|
||||
result.sons[1] = s
|
||||
result.sons[2] = loc
|
||||
|
||||
proc buildElse(n: PNode; o: Operators): PNode =
|
||||
var s = newNodeIT(nkCurly, n.info, settype(n[0]))
|
||||
for i in 1..<n.len-1:
|
||||
let branch = n[i]
|
||||
var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
|
||||
for i in 1..n.len-2:
|
||||
let branch = n.sons[i]
|
||||
assert branch.kind != nkElse
|
||||
if branch.kind == nkOfBranch:
|
||||
for j in 0..<branch.len-1:
|
||||
s.add(branch[j])
|
||||
for j in 0..branch.len-2:
|
||||
s.add(branch.sons[j])
|
||||
result = newNodeI(nkCall, n.info, 3)
|
||||
result[0] = newSymNode(o.opContains)
|
||||
result[1] = s
|
||||
result[2] = n[0]
|
||||
result.sons[0] = newSymNode(o.opContains)
|
||||
result.sons[1] = s
|
||||
result.sons[2] = n.sons[0]
|
||||
|
||||
proc addDiscriminantFact*(m: var TModel, n: PNode) =
|
||||
var fact = newNodeI(nkCall, n.info, 3)
|
||||
fact[0] = newSymNode(m.g.operators.opEq)
|
||||
fact[1] = n[0]
|
||||
fact[2] = n[1]
|
||||
fact.sons[0] = newSymNode(m.o.opEq)
|
||||
fact.sons[1] = n.sons[0]
|
||||
fact.sons[2] = n.sons[1]
|
||||
m.s.add fact
|
||||
|
||||
proc addAsgnFact*(m: var TModel, key, value: PNode) =
|
||||
var fact = newNodeI(nkCall, key.info, 3)
|
||||
fact[0] = newSymNode(m.g.operators.opEq)
|
||||
fact[1] = key
|
||||
fact[2] = value
|
||||
fact.sons[0] = newSymNode(m.o.opEq)
|
||||
fact.sons[1] = key
|
||||
fact.sons[2] = value
|
||||
m.s.add fact
|
||||
|
||||
proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||
@@ -1025,34 +1015,34 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||
# However, nil checking requires exactly the same mechanism! But for now
|
||||
# we simply use sameTree and live with the unsoundness of the analysis.
|
||||
var check = newNodeI(nkCall, a.info, 3)
|
||||
check[0] = newSymNode(m.g.operators.opEq)
|
||||
check[1] = a
|
||||
check[2] = b
|
||||
check.sons[0] = newSymNode(m.o.opEq)
|
||||
check.sons[1] = a
|
||||
check.sons[2] = b
|
||||
result = m.doesImply(check) == impYes
|
||||
|
||||
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
|
||||
let branch = n[i]
|
||||
let branch = n.sons[i]
|
||||
if branch.kind == nkOfBranch:
|
||||
m.s.add buildOf(branch, n[0], m.g.operators)
|
||||
m.s.add buildOf(branch, n.sons[0], m.o)
|
||||
else:
|
||||
m.s.add n.buildElse(m.g.operators).neg(m.g.operators)
|
||||
m.s.add n.buildElse(m.o).neg(m.o)
|
||||
|
||||
proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
if check.sons[1].kind == nkCurly:
|
||||
result = copyTree(check)
|
||||
if access.kind == nkDotExpr:
|
||||
var a = copyTree(access)
|
||||
a[1] = check[2]
|
||||
result[2] = a
|
||||
a.sons[1] = check.sons[2]
|
||||
result.sons[2] = a
|
||||
# 'access.kind != nkDotExpr' can happen for object constructors
|
||||
# which we don't check yet
|
||||
else:
|
||||
# it is some 'not'
|
||||
assert check.getMagic == mNot
|
||||
result = buildProperFieldCheck(access, check[1], o).neg(o)
|
||||
result = buildProperFieldCheck(access, check.sons[1], o).neg(o)
|
||||
|
||||
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
|
||||
for i in 1..<n.len:
|
||||
let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
|
||||
for i in 1..n.len-1:
|
||||
let check = buildProperFieldCheck(n.sons[0], n.sons[i], m.o)
|
||||
if check != nil and m.doesImply(check) != impYes:
|
||||
message(conf, n.info, warnProveField, renderTree(n[0])); break
|
||||
message(conf, n.info, warnProveField, renderTree(n.sons[0])); break
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
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
|
||||
internalAssert c.config, n.kind == nkCall and n.sons[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):
|
||||
if optHints in c.config.options and hintPattern in c.config.notes:
|
||||
rule = renderTree(n, {renderNoComments})
|
||||
let s = n[0].sym
|
||||
let s = n.sons[0].sym
|
||||
case s.kind
|
||||
of skMacro:
|
||||
result = semMacroExpr(c, n, orig, s)
|
||||
@@ -27,7 +27,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
result = semTemplateExpr(c, n, s, {efFromHlo})
|
||||
else:
|
||||
result = semDirectOp(c, n, {})
|
||||
if c.config.hasHint(hintPattern):
|
||||
if optHints in c.config.options and hintPattern in c.config.notes:
|
||||
message(c.config, orig.info, hintPattern, rule & " --> '" &
|
||||
renderTree(result, {renderNoComments}) & "'")
|
||||
|
||||
@@ -50,7 +50,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
c.patterns[i] = nil
|
||||
if x.kind == nkStmtList:
|
||||
assert x.len == 3
|
||||
x[1] = evalPattern(c, x[1], result)
|
||||
x.sons[1] = evalPattern(c, x.sons[1], result)
|
||||
result = flattenStmts(x)
|
||||
else:
|
||||
result = evalPattern(c, x, result)
|
||||
@@ -68,17 +68,17 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
else:
|
||||
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
|
||||
n[0].kind == nkSym and
|
||||
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
||||
n.sons[0].kind == nkSym and
|
||||
{sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
|
||||
# do not optimize 'var g {.global} = re(...)' again!
|
||||
return n
|
||||
result = applyPatterns(c, n)
|
||||
if result == n:
|
||||
# no optimization applied, try subtrees:
|
||||
for i in 0..<result.safeLen:
|
||||
let a = result[i]
|
||||
for i in 0 ..< safeLen(result):
|
||||
let a = result.sons[i]
|
||||
let h = hlo(c, a)
|
||||
if h != a: result[i] = h
|
||||
if h != a: result.sons[i] = h
|
||||
else:
|
||||
# perform type checking, so that the replacement still fits:
|
||||
if isEmptyType(n.typ) and isEmptyType(result.typ):
|
||||
@@ -86,9 +86,9 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
else:
|
||||
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 = commonOptimizations(c.graph, c.module, result)
|
||||
result = hlo(c, result)
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
result = commonOptimizations(c.graph, c.module, result)
|
||||
|
||||
proc hloBody(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
## A BiTable is a table that can be seen as an optimized pair
|
||||
## of (Table[LitId, Val], Table[Val, LitId]).
|
||||
|
||||
import hashes, rodfiles
|
||||
|
||||
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
|
||||
@@ -1,175 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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
|
||||
|
||||
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)
|
||||
@@ -1,161 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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]
|
||||
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)
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
====================================
|
||||
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.
|
||||
1222
compiler/ic/ic.nim
1222
compiler/ic/ic.nim
File diff suppressed because it is too large
Load Diff
@@ -1,149 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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
|
||||
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)
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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
|
||||
|
||||
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)
|
||||
@@ -1,417 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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]
|
||||
|
||||
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
|
||||
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||
# 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)
|
||||
@@ -1,147 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Module that contains code to replay global VM state changes and pragma
|
||||
## state like ``{.compile: "foo.c".}``. For IC (= Incremental compilation)
|
||||
## support.
|
||||
|
||||
import ".." / [ast, modulegraphs, trees, extccomp, btrees,
|
||||
msgs, lineinfos, pathutils, options, cgmeth]
|
||||
|
||||
import tables
|
||||
|
||||
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:
|
||||
assert n.kind == nkReplayAction
|
||||
# Fortunately only a tiny subset of the available pragmas need to
|
||||
# be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
|
||||
if n.len >= 2:
|
||||
internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
|
||||
case n[0].strVal
|
||||
of "hint": message(g.config, n.info, hintUser, n[1].strVal)
|
||||
of "warning": message(g.config, n.info, warnUser, n[1].strVal)
|
||||
of "error": localError(g.config, n.info, errUser, n[1].strVal)
|
||||
of "compile":
|
||||
internalAssert g.config, 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)
|
||||
of "inc":
|
||||
let destKey = n[1].strVal
|
||||
let by = n[2].intVal
|
||||
let v = getOrDefault(g.cacheCounters, destKey)
|
||||
g.cacheCounters[destKey] = v+by
|
||||
of "put":
|
||||
let destKey = n[1].strVal
|
||||
let key = n[2].strVal
|
||||
let val = n[3]
|
||||
if not contains(g.cacheTables, destKey):
|
||||
g.cacheTables[destKey] = initBTree[string, PNode]()
|
||||
if not contains(g.cacheTables[destKey], key):
|
||||
g.cacheTables[destKey].add(key, val)
|
||||
else:
|
||||
internalError(g.config, n.info, "key already exists: " & key)
|
||||
of "incl":
|
||||
let destKey = n[1].strVal
|
||||
let val = n[2]
|
||||
if not contains(g.cacheSeqs, destKey):
|
||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
||||
else:
|
||||
block search:
|
||||
for existing in g.cacheSeqs[destKey]:
|
||||
if exprStructuralEquivalent(existing, val, strictSymEquality=true):
|
||||
break search
|
||||
g.cacheSeqs[destKey].add val
|
||||
of "add":
|
||||
let destKey = n[1].strVal
|
||||
let val = n[2]
|
||||
if not contains(g.cacheSeqs, destKey):
|
||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
||||
else:
|
||||
g.cacheSeqs[destKey].add val
|
||||
else:
|
||||
internalAssert g.config, false
|
||||
|
||||
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))
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -1,247 +0,0 @@
|
||||
#
|
||||
#
|
||||
# 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
|
||||
|
||||
## 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
|
||||
macroUsagesSection
|
||||
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
|
||||
@@ -15,9 +15,12 @@ import
|
||||
hashes, wordrecg
|
||||
|
||||
type
|
||||
PIdent* = ref TIdent
|
||||
TIdent*{.acyclic.} = object
|
||||
TIdObj* = object of RootObj
|
||||
id*: int # unique id; use this for comparisons and not the pointers
|
||||
|
||||
PIdObj* = ref TIdObj
|
||||
PIdent* = ref TIdent
|
||||
TIdent*{.acyclic.} = object of TIdObj
|
||||
s*: string
|
||||
next*: PIdent # for hash-table chaining
|
||||
h*: Hash # hash value of s
|
||||
@@ -84,7 +87,7 @@ proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIden
|
||||
new(result)
|
||||
result.h = h
|
||||
result.s = newString(length)
|
||||
for i in 0..<length: result.s[i] = identifier[i]
|
||||
for i in 0 ..< length: result.s[i] = identifier[i]
|
||||
result.next = ic.buckets[idx]
|
||||
ic.buckets[idx] = result
|
||||
if id == 0:
|
||||
@@ -94,11 +97,11 @@ proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIden
|
||||
result.id = id
|
||||
|
||||
proc getIdent*(ic: IdentCache; identifier: string): PIdent =
|
||||
result = getIdent(ic, cstring(identifier), identifier.len,
|
||||
result = getIdent(ic, cstring(identifier), len(identifier),
|
||||
hashIgnoreStyle(identifier))
|
||||
|
||||
proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent =
|
||||
result = getIdent(ic, cstring(identifier), identifier.len, h)
|
||||
result = getIdent(ic, cstring(identifier), len(identifier), h)
|
||||
|
||||
proc newIdentCache*(): IdentCache =
|
||||
result = IdentCache()
|
||||
@@ -107,14 +110,9 @@ proc newIdentCache*(): IdentCache =
|
||||
result.idDelegator = result.getIdent":delegator"
|
||||
result.emptyIdent = result.getIdent("")
|
||||
# initialize the keywords:
|
||||
for s in succ(low(TSpecialWord))..high(TSpecialWord):
|
||||
result.getIdent($s, hashIgnoreStyle($s)).id = ord(s)
|
||||
for s in succ(low(specialWords)) .. high(specialWords):
|
||||
result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
|
||||
|
||||
proc whichKeyword*(id: PIdent): TSpecialWord =
|
||||
if id.id < 0: result = wInvalid
|
||||
else: result = TSpecialWord(id.id)
|
||||
|
||||
proc hash*(x: PIdent): Hash {.inline.} = x.h
|
||||
proc `==`*(a, b: PIdent): bool {.inline.} =
|
||||
if a.isNil or b.isNil: result = system.`==`(a, b)
|
||||
else: result = a.id == b.id
|
||||
|
||||
59
compiler/idgen.nim
Normal file
59
compiler/idgen.nim
Normal file
@@ -0,0 +1,59 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains a simple persistent id generator.
|
||||
|
||||
import idents, strutils, options, pathutils
|
||||
|
||||
var gFrontEndId*: int
|
||||
|
||||
const
|
||||
debugIds* = false
|
||||
|
||||
when debugIds:
|
||||
import intsets
|
||||
|
||||
var usedIds = initIntSet()
|
||||
|
||||
proc registerID*(id: PIdObj) =
|
||||
when debugIds:
|
||||
if id.id == -1 or containsOrIncl(usedIds, id.id):
|
||||
internalError("ID already used: " & $id.id)
|
||||
|
||||
proc getID*(): int {.inline.} =
|
||||
result = gFrontEndId
|
||||
inc(gFrontEndId)
|
||||
|
||||
proc setId*(id: int) {.inline.} =
|
||||
gFrontEndId = max(gFrontEndId, id + 1)
|
||||
|
||||
proc idSynchronizationPoint*(idRange: int) =
|
||||
gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
|
||||
|
||||
proc toGid(conf: ConfigRef; f: AbsoluteFile): string =
|
||||
# we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
|
||||
# will cause strange bugs if multiple projects are in the same folder, so
|
||||
# we simply use a project independent name:
|
||||
result = options.completeGeneratedFilePath(conf, AbsoluteFile"nim.gid").string
|
||||
|
||||
proc saveMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
|
||||
var f = open(toGid(conf, project), fmWrite)
|
||||
f.writeLine($gFrontEndId)
|
||||
f.close()
|
||||
|
||||
proc loadMaxIds*(conf: ConfigRef; project: AbsoluteFile) =
|
||||
var f: File
|
||||
if open(f, toGid(conf, project), fmRead):
|
||||
var line = newStringOfCap(20)
|
||||
if f.readLine(line):
|
||||
var frontEndId = parseInt(line)
|
||||
if f.readLine(line):
|
||||
# var backEndId = parseInt(line)
|
||||
gFrontEndId = max(gFrontEndId, frontEndId)
|
||||
f.close()
|
||||
@@ -11,124 +11,70 @@
|
||||
|
||||
import
|
||||
intsets, ast, astalgo, msgs, options, idents, lookups,
|
||||
semdata, modulepaths, sigmatch, lineinfos, sets,
|
||||
modulegraphs, wordrecg, tables
|
||||
from strutils import `%`
|
||||
semdata, modulepaths, sigmatch, lineinfos, sets
|
||||
|
||||
proc readExceptSet*(c: PContext, n: PNode): IntSet =
|
||||
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
|
||||
result = initIntSet()
|
||||
for i in 1..<n.len:
|
||||
for i in 1 ..< n.len:
|
||||
let ident = lookups.considerQuotedIdent(c, n[i])
|
||||
result.incl(ident.id)
|
||||
|
||||
proc declarePureEnumField*(c: PContext; s: PSym) =
|
||||
# XXX Remove the outer 'if' statement and see what breaks.
|
||||
var amb = false
|
||||
if someSymFromImportTable(c, s.name, amb) == nil:
|
||||
strTableAdd(c.pureEnumFields, s)
|
||||
when false:
|
||||
let checkB = strTableGet(c.pureEnumFields, s.name)
|
||||
if checkB == nil:
|
||||
strTableAdd(c.pureEnumFields, s)
|
||||
when false:
|
||||
proc importPureEnumField*(c: PContext; s: PSym) =
|
||||
let check = strTableGet(c.importTable.symbols, s.name)
|
||||
if check == nil:
|
||||
let checkB = strTableGet(c.pureEnumFields, s.name)
|
||||
if checkB == nil:
|
||||
strTableAdd(c.pureEnumFields, s)
|
||||
else:
|
||||
# mark as ambiguous:
|
||||
incl(c.ambiguousSymbols, checkB.id)
|
||||
incl(c.ambiguousSymbols, s.id)
|
||||
|
||||
proc importPureEnumField(c: PContext; s: PSym) =
|
||||
var amb = false
|
||||
if someSymFromImportTable(c, s.name, amb) == nil:
|
||||
strTableAdd(c.pureEnumFields, s)
|
||||
when false:
|
||||
let checkB = strTableGet(c.pureEnumFields, s.name)
|
||||
if checkB == nil:
|
||||
strTableAdd(c.pureEnumFields, s)
|
||||
when false:
|
||||
# mark as ambiguous:
|
||||
incl(c.ambiguousSymbols, checkB.id)
|
||||
incl(c.ambiguousSymbols, s.id)
|
||||
|
||||
proc importPureEnumFields(c: PContext; s: PSym; etyp: PType) =
|
||||
assert sfPure in s.flags
|
||||
for j in 0..<etyp.n.len:
|
||||
var e = etyp.n[j].sym
|
||||
if e.kind != skEnumField:
|
||||
internalError(c.config, s.info, "rawImportSymbol")
|
||||
# BUGFIX: because of aliases for enums the symbol may already
|
||||
# have been put into the symbol table
|
||||
# BUGFIX: but only iff they are the same symbols!
|
||||
for check in importedItems(c, e.name):
|
||||
if check.id == e.id:
|
||||
e = nil
|
||||
break
|
||||
if e != nil:
|
||||
importPureEnumField(c, e)
|
||||
|
||||
proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
|
||||
proc rawImportSymbol(c: PContext, s, origin: PSym) =
|
||||
# This does not handle stubs, because otherwise loading on demand would be
|
||||
# pointless in practice. So importing stubs is fine here!
|
||||
# check if we have already a symbol of the same name:
|
||||
when false:
|
||||
var check = someSymFromImportTable(c, s.name)
|
||||
if check != nil and check.id != s.id:
|
||||
if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
|
||||
# s and check need to be qualified:
|
||||
incl(c.ambiguousSymbols, s.id)
|
||||
incl(c.ambiguousSymbols, check.id)
|
||||
var check = strTableGet(c.importTable.symbols, s.name)
|
||||
if check != nil and check.id != s.id:
|
||||
if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
|
||||
# s and check need to be qualified:
|
||||
incl(c.ambiguousSymbols, s.id)
|
||||
incl(c.ambiguousSymbols, check.id)
|
||||
# thanks to 'export' feature, it could be we import the same symbol from
|
||||
# multiple sources, so we need to call 'strTableAdd' here:
|
||||
when false:
|
||||
# now lazy. Speeds up the compiler and is a prerequisite for IC.
|
||||
strTableAdd(c.importTable.symbols, s)
|
||||
else:
|
||||
importSet.incl s.id
|
||||
# multiple sources, so we need to call 'StrTableAdd' here:
|
||||
strTableAdd(c.importTable.symbols, s)
|
||||
if s.kind == skType:
|
||||
var etyp = s.typ
|
||||
if etyp.kind in {tyBool, tyEnum}:
|
||||
for j in 0..<etyp.n.len:
|
||||
var e = etyp.n[j].sym
|
||||
for j in 0 ..< len(etyp.n):
|
||||
var e = etyp.n.sons[j].sym
|
||||
if e.kind != skEnumField:
|
||||
internalError(c.config, s.info, "rawImportSymbol")
|
||||
# BUGFIX: because of aliases for enums the symbol may already
|
||||
# have been put into the symbol table
|
||||
# BUGFIX: but only iff they are the same symbols!
|
||||
for check in importedItems(c, e.name):
|
||||
var it: TIdentIter
|
||||
check = initIdentIter(it, c.importTable.symbols, e.name)
|
||||
while check != nil:
|
||||
if check.id == e.id:
|
||||
e = nil
|
||||
break
|
||||
check = nextIdentIter(it, c.importTable.symbols)
|
||||
if e != nil:
|
||||
if sfPure notin s.flags:
|
||||
rawImportSymbol(c, e, origin, importSet)
|
||||
rawImportSymbol(c, e, origin)
|
||||
else:
|
||||
importPureEnumField(c, e)
|
||||
else:
|
||||
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
|
||||
if hasPattern(s): addPattern(c, LazySym(sym: s))
|
||||
if s.kind == skConverter: addConverter(c, s)
|
||||
if hasPattern(s): addPattern(c, 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")
|
||||
if n.kind == nkPragmaExpr:
|
||||
if n.len == 2 and n[1].kind == nkPragma:
|
||||
result[0] = n[0]
|
||||
for ni in n[1]:
|
||||
if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident)
|
||||
else: bail()
|
||||
else: bail()
|
||||
else:
|
||||
result[0] = n
|
||||
if result[0].safeLen > 0:
|
||||
(result[0][^1], result[1]) = splitPragmas(c, result[0][^1])
|
||||
|
||||
proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
||||
let (n, kws) = splitPragmas(c, n)
|
||||
if kws.len > 0:
|
||||
globalError(c.config, n.info, "unexpected pragma")
|
||||
|
||||
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||
let ident = lookups.considerQuotedIdent(c, n)
|
||||
let s = someSym(c.graph, fromMod, ident)
|
||||
let s = strTableGet(fromMod.tab, ident)
|
||||
if s == nil:
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
||||
else:
|
||||
@@ -138,77 +84,34 @@ 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
|
||||
var e = initModuleIter(it, c.graph, fromMod, s.name)
|
||||
var it: TIdentIter
|
||||
var e = initIdentIter(it, fromMod.tab, s.name)
|
||||
while e != nil:
|
||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
||||
if s.kind in ExportableSymKinds:
|
||||
rawImportSymbol(c, e, fromMod, importSet)
|
||||
e = nextModuleIter(it, c.graph)
|
||||
rawImportSymbol(c, e, fromMod)
|
||||
e = nextIdentIter(it, fromMod.tab)
|
||||
else:
|
||||
rawImportSymbol(c, s, fromMod, importSet)
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
|
||||
proc addImport(c: PContext; im: sink ImportedModule) =
|
||||
for i in 0..high(c.imports):
|
||||
if c.imports[i].m == im.m:
|
||||
# we have already imported the module: Check which import
|
||||
# is more "powerful":
|
||||
case c.imports[i].mode
|
||||
of importAll: discard "already imported all symbols"
|
||||
of importSet:
|
||||
case im.mode
|
||||
of importAll, importExcept:
|
||||
# XXX: slightly wrong semantics for 'importExcept'...
|
||||
# But we should probably change the spec and disallow this case.
|
||||
c.imports[i] = im
|
||||
of importSet:
|
||||
# merge the import sets:
|
||||
c.imports[i].imported.incl im.imported
|
||||
of importExcept:
|
||||
case im.mode
|
||||
of importAll:
|
||||
c.imports[i] = im
|
||||
of importSet:
|
||||
discard
|
||||
of importExcept:
|
||||
var cut = initIntSet()
|
||||
# only exclude what is consistent between the two sets:
|
||||
for j in im.exceptSet:
|
||||
if j in c.imports[i].exceptSet:
|
||||
cut.incl j
|
||||
c.imports[i].exceptSet = cut
|
||||
return
|
||||
c.imports.add im
|
||||
|
||||
template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
|
||||
for it in mitems c.graph.ifaces[fromMod.position].converters:
|
||||
if filter:
|
||||
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:
|
||||
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:
|
||||
loadPackedSym(c.graph, it)
|
||||
importPureEnumFields(c, it.sym, it.sym.typ)
|
||||
rawImportSymbol(c, s, fromMod)
|
||||
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
|
||||
|
||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
|
||||
addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
|
||||
var i: TTabIter
|
||||
var s = initTabIter(i, fromMod.tab)
|
||||
while s != nil:
|
||||
if s.kind != skModule:
|
||||
if s.kind != skEnumField:
|
||||
if s.kind notin ExportableSymKinds:
|
||||
internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s)
|
||||
if exceptSet.isNil or s.name.id notin exceptSet:
|
||||
rawImportSymbol(c, s, fromMod)
|
||||
s = nextIter(i, fromMod.tab)
|
||||
|
||||
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
||||
c.addImport ImportedModule(m: fromMod, mode: importAll)
|
||||
addUnnamedIt(c, fromMod, true)
|
||||
when false:
|
||||
var exceptSet: IntSet
|
||||
importAllSymbolsExcept(c, fromMod, exceptSet)
|
||||
var exceptSet: IntSet
|
||||
importAllSymbolsExcept(c, fromMod, exceptSet)
|
||||
|
||||
proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
|
||||
proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym) =
|
||||
if n.isNil: return
|
||||
case n.kind
|
||||
of nkExportStmt:
|
||||
@@ -218,108 +121,82 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
if s.kind == skModule:
|
||||
importAllSymbolsExcept(c, s, exceptSet)
|
||||
elif exceptSet.isNil or s.name.id notin exceptSet:
|
||||
rawImportSymbol(c, s, fromMod, importSet)
|
||||
rawImportSymbol(c, s, fromMod)
|
||||
of nkExportExceptStmt:
|
||||
localError(c.config, n.info, "'export except' not implemented")
|
||||
else:
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
for i in 0..safeLen(n)-1:
|
||||
importForwarded(c, n.sons[i], exceptSet, fromMod)
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
|
||||
c.unusedImports.add((realModule, n.info))
|
||||
if n.kind != nkImportAs: discard
|
||||
elif n.len != 2 or n[1].kind != nkIdent:
|
||||
elif n.len != 2 or n.sons[1].kind != nkIdent:
|
||||
localError(c.config, n.info, "module alias must be an identifier")
|
||||
elif n[1].ident.id != realModule.name.id:
|
||||
elif n.sons[1].ident.id != realModule.name.id:
|
||||
# some misguided guy will write 'import abc.foo as foo' ...
|
||||
result = createModuleAliasImpl(n[1].ident)
|
||||
if result == realModule:
|
||||
# avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
c.unusedImports.add((result, n.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
|
||||
c.config.options)
|
||||
|
||||
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
|
||||
var ret: typeof(result)
|
||||
proc processPragma(n2: PNode): PNode =
|
||||
let (result2, kws) = splitPragmas(c, n2)
|
||||
result = result2
|
||||
for ai in kws:
|
||||
case ai
|
||||
of wImportHidden: ret.importHidden = true
|
||||
else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden})
|
||||
|
||||
if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
|
||||
ret.node = newNodeI(nkImportAs, n.info)
|
||||
ret.node.add n[1].processPragma
|
||||
ret.node.add n[2]
|
||||
else:
|
||||
ret.node = n.processPragma
|
||||
return ret
|
||||
|
||||
proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
let transf = transformImportAs(c, n)
|
||||
n = transf.node
|
||||
proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
|
||||
let f = checkModuleName(c.config, n)
|
||||
if f != InvalidFileIdx:
|
||||
addImportFileDep(c, f)
|
||||
let L = c.graph.importStack.len
|
||||
let recursion = c.graph.importStack.find(f)
|
||||
c.graph.importStack.add f
|
||||
#echo "adding ", toFullPath(f), " at ", L+1
|
||||
if recursion >= 0:
|
||||
var err = ""
|
||||
for i in recursion..<L:
|
||||
for i in recursion ..< L:
|
||||
if i > recursion: err.add "\n"
|
||||
err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
|
||||
toFullPath(c.config, c.graph.importStack[i+1])
|
||||
c.recursiveDep = err
|
||||
|
||||
var realModule: PSym
|
||||
discard pushOptionEntry(c)
|
||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
||||
result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f))
|
||||
popOptionEntry(c)
|
||||
|
||||
#echo "set back to ", L
|
||||
c.graph.importStack.setLen(L)
|
||||
# we cannot perform this check reliably because of
|
||||
# test: modules/import_in_config) # xxx is that still true?
|
||||
if realModule == c.module:
|
||||
localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s)
|
||||
if sfDeprecated in realModule.flags:
|
||||
var prefix = ""
|
||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
||||
# test: modules/import_in_config)
|
||||
when true:
|
||||
if result.info.fileIndex == c.module.info.fileIndex and
|
||||
result.info.fileIndex == n.info.fileIndex:
|
||||
localError(c.config, n.info, "A module cannot import itself")
|
||||
if sfDeprecated in result.flags:
|
||||
if result.constraint != nil:
|
||||
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
|
||||
else:
|
||||
message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
|
||||
suggestSym(c.config, n.info, result, c.graph.usageSym, false)
|
||||
importStmtResult.add newSymNode(result, n.info)
|
||||
#newStrNode(toFullPath(c.config, f), n.info)
|
||||
|
||||
proc afterImport(c: PContext, m: PSym) =
|
||||
# fixes bug #17510, for re-exported symbols
|
||||
let realModuleId = c.importModuleMap[m.id]
|
||||
for s in allSyms(c.graph, m):
|
||||
if s.owner.id != realModuleId:
|
||||
c.exportIndirections.incl((m.id, s.id))
|
||||
proc transformImportAs(c: PContext; n: PNode): PNode =
|
||||
if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
|
||||
result = newNodeI(nkImportAs, n.info)
|
||||
result.add n.sons[1]
|
||||
result.add n.sons[2]
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||
var it = it
|
||||
let it = transformImportAs(c, it)
|
||||
let m = myImportModule(c, it, importStmtResult)
|
||||
if m != nil:
|
||||
var emptySet: IntSet
|
||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
||||
importAllSymbols(c, m)
|
||||
importAllSymbolsExcept(c, m, emptySet)
|
||||
#importForwarded(c, m.ast, emptySet, m)
|
||||
afterImport(c, m)
|
||||
|
||||
proc evalImport*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkImportStmt, n.info)
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
for i in 0 ..< len(n):
|
||||
let it = n.sons[i]
|
||||
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
|
||||
let sep = it[0]
|
||||
let dir = it[1]
|
||||
@@ -329,13 +206,13 @@ proc evalImport*(c: PContext, n: PNode): PNode =
|
||||
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":
|
||||
if x.kind == nkInfix and x.sons[0].ident.s == "as":
|
||||
let impAs = copyTree(x)
|
||||
imp[2] = x[1]
|
||||
impAs[1] = imp
|
||||
imp.sons[2] = x.sons[1]
|
||||
impAs.sons[1] = imp
|
||||
impMod(c, imp, result)
|
||||
else:
|
||||
imp[2] = x
|
||||
imp.sons[2] = x
|
||||
impMod(c, imp, result)
|
||||
else:
|
||||
impMod(c, it, result)
|
||||
@@ -343,25 +220,22 @@ proc evalImport*(c: PContext, n: PNode): PNode =
|
||||
proc evalFrom*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkImportStmt, n.info)
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
var m = myImportModule(c, n[0], result)
|
||||
n.sons[0] = transformImportAs(c, n.sons[0])
|
||||
var m = myImportModule(c, n.sons[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
n.sons[0] = newSymNode(m)
|
||||
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:
|
||||
if n[i].kind != nkNilLit:
|
||||
importSymbol(c, n[i], m, im.imported)
|
||||
c.addImport im
|
||||
afterImport(c, m)
|
||||
for i in 1 ..< len(n):
|
||||
if n.sons[i].kind != nkNilLit:
|
||||
importSymbol(c, n.sons[i], m)
|
||||
|
||||
proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkImportStmt, n.info)
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
var m = myImportModule(c, n[0], result)
|
||||
n.sons[0] = transformImportAs(c, n.sons[0])
|
||||
var m = myImportModule(c, n.sons[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
n.sons[0] = newSymNode(m)
|
||||
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)
|
||||
|
||||
198
compiler/incremental.nim
Normal file
198
compiler/incremental.nim
Normal file
@@ -0,0 +1,198 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2018 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Basic type definitions the module graph needs in order to support
|
||||
## incremental compilations.
|
||||
|
||||
const nimIncremental* = defined(nimIncremental)
|
||||
|
||||
import options, lineinfos
|
||||
|
||||
when nimIncremental:
|
||||
import ast, msgs, intsets, btrees, db_sqlite, std / sha1, pathutils
|
||||
from strutils import parseInt
|
||||
from os import isAbsolute
|
||||
|
||||
type
|
||||
Writer* = object
|
||||
sstack*: seq[PSym] # a stack of symbols to process
|
||||
tstack*: seq[PType] # a stack of types to process
|
||||
tmarks*, smarks*: IntSet
|
||||
forwardedSyms*: seq[PSym]
|
||||
|
||||
Reader* = object
|
||||
syms*: BTree[int, PSym]
|
||||
types*: BTree[int, PType]
|
||||
|
||||
IncrementalCtx* = object
|
||||
db*: DbConn
|
||||
w*: Writer
|
||||
r*: Reader
|
||||
configChanged*: bool
|
||||
|
||||
proc init*(incr: var IncrementalCtx) =
|
||||
incr.w.sstack = @[]
|
||||
incr.w.tstack = @[]
|
||||
incr.w.tmarks = initIntSet()
|
||||
incr.w.smarks = initIntSet()
|
||||
incr.w.forwardedSyms = @[]
|
||||
incr.r.syms = initBTree[int, PSym]()
|
||||
incr.r.types = initBTree[int, PType]()
|
||||
|
||||
|
||||
proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: AbsoluteFile): string =
|
||||
result = msgs.getHash(conf, fileIdx)
|
||||
if result.len == 0 and isAbsolute(string fullpath):
|
||||
result = $secureHashFile(string fullpath)
|
||||
msgs.setHash(conf, fileIdx, result)
|
||||
|
||||
proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int =
|
||||
if fileIdx == FileIndex(-1): return -1
|
||||
let fullpath = toFullPath(conf, fileIdx)
|
||||
let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
|
||||
fullpath)
|
||||
let id = row[0]
|
||||
let fullhash = hashFileCached(conf, fileIdx, AbsoluteFile fullpath)
|
||||
if id.len == 0:
|
||||
result = int incr.db.insertID(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)",
|
||||
int(fileIdx), fullpath, fullhash)
|
||||
else:
|
||||
if row[1] != fullhash:
|
||||
incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
|
||||
result = parseInt(id)
|
||||
|
||||
proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex =
|
||||
if dbId == -1: return FileIndex(-1)
|
||||
let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId)
|
||||
doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
|
||||
result = fileInfoIdx(conf, AbsoluteFile fullpath)
|
||||
|
||||
|
||||
proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
|
||||
module, fileIdx: FileIndex;
|
||||
isIncludeFile: bool) =
|
||||
if conf.symbolFiles != v2Sf: return
|
||||
|
||||
let a = toDbFileId(incr, conf, module)
|
||||
let b = toDbFileId(incr, conf, fileIdx)
|
||||
|
||||
incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
|
||||
a, b, ord(isIncludeFile))
|
||||
|
||||
# --------------- Database model ---------------------------------------------
|
||||
|
||||
proc createDb*(db: DbConn) =
|
||||
db.exec(sql"""
|
||||
create table if not exists controlblock(
|
||||
idgen integer not null
|
||||
);
|
||||
""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists config(
|
||||
config varchar(8000) not null
|
||||
);
|
||||
""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists filenames(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
fullpath varchar(8000) not null,
|
||||
fullHash varchar(256) not null
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists modules(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
fullpath varchar(8000) not null,
|
||||
interfHash varchar(256) not null,
|
||||
fullHash varchar(256) not null,
|
||||
|
||||
created timestamp not null default (DATETIME('now'))
|
||||
);""")
|
||||
db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists deps(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
dependency integer not null,
|
||||
isIncludeFile integer not null,
|
||||
foreign key (module) references filenames(id),
|
||||
foreign key (dependency) references filenames(id)
|
||||
);""")
|
||||
db.exec(sql"""create index if not exists DepsIx on deps(module);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists types(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
module integer not null,
|
||||
data blob not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index TypeByModuleIdx on types(module);"
|
||||
db.exec sql"create index TypeByNimIdIdx on types(nimid);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists syms(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
module integer not null,
|
||||
name varchar(256) not null,
|
||||
data blob not null,
|
||||
exported int not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index if not exists SymNameIx on syms(name);"
|
||||
db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
|
||||
db.exec sql"create index SymByModuleIdx on syms(module);"
|
||||
db.exec sql"create index SymByNimIdIdx on syms(nimid);"
|
||||
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists toplevelstmts(
|
||||
id integer primary key,
|
||||
position integer not null,
|
||||
module integer not null,
|
||||
data blob not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
|
||||
db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists statics(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
data blob not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
|
||||
db.exec sql"insert into controlblock(idgen) values (0)"
|
||||
|
||||
|
||||
else:
|
||||
type
|
||||
IncrementalCtx* = object
|
||||
|
||||
template init*(incr: IncrementalCtx) = discard
|
||||
|
||||
template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
|
||||
module, fileIdx: FileIndex;
|
||||
isIncludeFile: bool) =
|
||||
discard
|
||||
@@ -1,18 +0,0 @@
|
||||
##[
|
||||
This module only exists to generate docs for the compiler.
|
||||
|
||||
## links
|
||||
* [main docs](../lib.html)
|
||||
* [compiler user guide](../nimc.html)
|
||||
* [Internals of the Nim Compiler](../intern.html)
|
||||
]##
|
||||
|
||||
#[
|
||||
note: this is named `index` so that navigating to https://nim-lang.github.io/Nim/compiler/
|
||||
will work.
|
||||
|
||||
xxx this should also import other modules, not transitively imported by `compiler/nim.nim`,
|
||||
eg `evalffi`, otherwise these aren't shown. A glob could be used at CT.
|
||||
]#
|
||||
|
||||
import nim
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,6 @@ Platforms: """
|
||||
netbsd: i386;amd64
|
||||
openbsd: i386;amd64;arm;arm64
|
||||
dragonfly: i386;amd64
|
||||
crossos: amd64
|
||||
haiku: i386;amd64
|
||||
android: i386;arm;arm64
|
||||
nintendoswitch: arm64
|
||||
@@ -36,8 +35,9 @@ App: Console
|
||||
License: "copying.txt"
|
||||
|
||||
[Config]
|
||||
Files: "config/*.cfg"
|
||||
Files: "config/config.nims"
|
||||
Files: "config/nim.cfg"
|
||||
Files: "config/nimdoc.cfg"
|
||||
Files: "config/nimdoc.tex.cfg"
|
||||
|
||||
[Documentation]
|
||||
; Files: "doc/*.html"
|
||||
@@ -110,7 +110,6 @@ Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/downlo
|
||||
Files: "bin/makelink.exe"
|
||||
Files: "bin/7zG.exe"
|
||||
Files: "bin/*.dll"
|
||||
Files: "bin/cacert.pem"
|
||||
|
||||
[UnixBin]
|
||||
Files: "bin/nim"
|
||||
@@ -120,6 +119,7 @@ Files: "bin/nim"
|
||||
InstallScript: "yes"
|
||||
UninstallScript: "yes"
|
||||
Files: "bin/nim-gdb"
|
||||
Files: "bin/nim-gdb.bash"
|
||||
|
||||
|
||||
[InnoSetup]
|
||||
@@ -141,5 +141,5 @@ shortDesc: "The Nim Compiler"
|
||||
licenses: "bin/nim,MIT;lib/*,MIT;"
|
||||
|
||||
[nimble]
|
||||
pkgName: "nim"
|
||||
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
|
||||
pkgName: "compiler"
|
||||
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
## This module is for compiler internal use only. For reliable error
|
||||
## messages and range checks, the compiler needs a data type that can
|
||||
## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
|
||||
## hold all from ``low(BiggestInt)`` to ``high(BiggestUInt)``, This
|
||||
## type is for that purpose.
|
||||
|
||||
from math import trunc
|
||||
|
||||
type
|
||||
Int128* = object
|
||||
udata: array[4, uint32]
|
||||
udata: array[4,uint32]
|
||||
|
||||
template sdata(arg: Int128, idx: int): int32 =
|
||||
# udata and sdata was supposed to be in a union, but unions are
|
||||
@@ -17,39 +17,39 @@ template sdata(arg: Int128, idx: int): int32 =
|
||||
# encoding least significant int first (like LittleEndian)
|
||||
|
||||
const
|
||||
Zero* = Int128(udata: [0'u32, 0, 0, 0])
|
||||
One* = Int128(udata: [1'u32, 0, 0, 0])
|
||||
Ten* = Int128(udata: [10'u32, 0, 0, 0])
|
||||
Min = Int128(udata: [0'u32, 0, 0, 0x80000000'u32])
|
||||
Max = Int128(udata: [high(uint32), high(uint32), high(uint32), uint32(high(int32))])
|
||||
NegOne* = Int128(udata: [0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32])
|
||||
Zero* = Int128(udata: [0'u32,0,0,0])
|
||||
One* = Int128(udata: [1'u32,0,0,0])
|
||||
Ten* = Int128(udata: [10'u32,0,0,0])
|
||||
Min = Int128(udata: [0'u32,0,0,0x80000000'u32])
|
||||
Max = Int128(udata: [high(uint32),high(uint32),high(uint32),uint32(high(int32))])
|
||||
NegOne* = Int128(udata: [0xffffffff'u32,0xffffffff'u32,0xffffffff'u32,0xffffffff'u32])
|
||||
|
||||
template low*(t: typedesc[Int128]): Int128 = Min
|
||||
template high*(t: typedesc[Int128]): Int128 = Max
|
||||
|
||||
proc `$`*(a: Int128): string
|
||||
|
||||
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
|
||||
when T is bool: result.sdata(0) = int32(arg)
|
||||
elif T is SomeUnsignedInt:
|
||||
proc toInt128*[T: SomeInteger](arg: T): Int128 =
|
||||
when T is SomeUnsignedInt:
|
||||
when sizeof(arg) <= 4:
|
||||
result.udata[0] = uint32(arg)
|
||||
else:
|
||||
result.udata[0] = uint32(arg and T(0xffffffff))
|
||||
result.udata[1] = uint32(arg shr 32)
|
||||
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
|
||||
when sizeof(arg) <= 4:
|
||||
result.sdata(0) = int32(arg)
|
||||
if arg < 0: # sign extend
|
||||
result.sdata(1) = -1
|
||||
result.sdata(2) = -1
|
||||
result.sdata(3) = -1
|
||||
else:
|
||||
let tmp = int64(arg)
|
||||
result.udata[0] = uint32(tmp and 0xffffffff)
|
||||
result.sdata(1) = int32(tmp shr 32)
|
||||
if arg < 0: # sign extend
|
||||
result.sdata(2) = -1
|
||||
result.sdata(3) = -1
|
||||
|
||||
template isNegative(arg: Int128): bool =
|
||||
arg.sdata(3) < 0
|
||||
@@ -57,10 +57,10 @@ template isNegative(arg: Int128): bool =
|
||||
template isNegative(arg: int32): bool =
|
||||
arg < 0
|
||||
|
||||
proc bitconcat(a, b: uint32): uint64 =
|
||||
proc bitconcat(a,b: uint32): uint64 =
|
||||
(uint64(a) shl 32) or uint64(b)
|
||||
|
||||
proc bitsplit(a: uint64): (uint32, uint32) =
|
||||
proc bitsplit(a: uint64): (uint32,uint32) =
|
||||
(cast[uint32](a shr 32), cast[uint32](a))
|
||||
|
||||
proc toInt64*(arg: Int128): int64 =
|
||||
@@ -162,7 +162,7 @@ proc castToUInt64*(arg: Int128): uint64 =
|
||||
cast[uint64](bitconcat(arg.udata[1], arg.udata[0]))
|
||||
|
||||
proc addToHex(result: var string; arg: uint32) =
|
||||
for i in 0..<8:
|
||||
for i in 0 ..< 8:
|
||||
let idx = (arg shr ((7-i) * 4)) and 0xf
|
||||
result.add "0123456789abcdef"[idx]
|
||||
|
||||
@@ -176,6 +176,7 @@ proc toHex*(arg: Int128): string =
|
||||
result.addToHex(arg)
|
||||
|
||||
proc inc*(a: var Int128, y: uint32 = 1) =
|
||||
let input = a
|
||||
a.udata[0] += y
|
||||
if unlikely(a.udata[0] < y):
|
||||
a.udata[1].inc
|
||||
@@ -185,7 +186,7 @@ proc inc*(a: var Int128, y: uint32 = 1) =
|
||||
a.udata[3].inc
|
||||
doAssert(a.sdata(3) != low(int32), "overflow")
|
||||
|
||||
proc cmp*(a, b: Int128): int =
|
||||
proc cmp*(a,b: Int128): int =
|
||||
let tmp1 = cmp(a.sdata(3), b.sdata(3))
|
||||
if tmp1 != 0: return tmp1
|
||||
let tmp2 = cmp(a.udata[2], b.udata[2])
|
||||
@@ -195,13 +196,13 @@ proc cmp*(a, b: Int128): int =
|
||||
let tmp4 = cmp(a.udata[0], b.udata[0])
|
||||
return tmp4
|
||||
|
||||
proc `<`*(a, b: Int128): bool =
|
||||
cmp(a, b) < 0
|
||||
proc `<`*(a,b: Int128): bool =
|
||||
cmp(a,b) < 0
|
||||
|
||||
proc `<=`*(a, b: Int128): bool =
|
||||
cmp(a, b) <= 0
|
||||
proc `<=`*(a,b: Int128): bool =
|
||||
cmp(a,b) <= 0
|
||||
|
||||
proc `==`*(a, b: Int128): bool =
|
||||
proc `==`*(a,b: Int128): bool =
|
||||
if a.udata[0] != b.udata[0]: return false
|
||||
if a.udata[1] != b.udata[1]: return false
|
||||
if a.udata[2] != b.udata[2]: return false
|
||||
@@ -220,19 +221,19 @@ proc bitnot*(a: Int128): Int128 =
|
||||
result.udata[2] = not a.udata[2]
|
||||
result.udata[3] = not a.udata[3]
|
||||
|
||||
proc bitand*(a, b: Int128): Int128 =
|
||||
proc bitand*(a,b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] and b.udata[0]
|
||||
result.udata[1] = a.udata[1] and b.udata[1]
|
||||
result.udata[2] = a.udata[2] and b.udata[2]
|
||||
result.udata[3] = a.udata[3] and b.udata[3]
|
||||
|
||||
proc bitor*(a, b: Int128): Int128 =
|
||||
proc bitor*(a,b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] or b.udata[0]
|
||||
result.udata[1] = a.udata[1] or b.udata[1]
|
||||
result.udata[2] = a.udata[2] or b.udata[2]
|
||||
result.udata[3] = a.udata[3] or b.udata[3]
|
||||
|
||||
proc bitxor*(a, b: Int128): Int128 =
|
||||
proc bitxor*(a,b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] xor b.udata[0]
|
||||
result.udata[1] = a.udata[1] xor b.udata[1]
|
||||
result.udata[2] = a.udata[2] xor b.udata[2]
|
||||
@@ -249,14 +250,14 @@ proc `shr`*(a: Int128, b: int): Int128 =
|
||||
if isNegative(a):
|
||||
result.sdata(3) = -1
|
||||
result.sdata(2) = a.sdata(3) shr (b and 31)
|
||||
result.udata[1] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr (b and 31))
|
||||
result.udata[1] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr (b and 31))
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr (b and 31))
|
||||
elif b < 96:
|
||||
if isNegative(a):
|
||||
result.sdata(3) = -1
|
||||
result.sdata(2) = -1
|
||||
result.sdata(1) = a.sdata(3) shr (b and 31)
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr (b and 31))
|
||||
else: # b < 128
|
||||
if isNegative(a):
|
||||
result.sdata(3) = -1
|
||||
@@ -287,7 +288,7 @@ proc `shl`*(a: Int128, b: int): Int128 =
|
||||
result.udata[2] = 0
|
||||
result.udata[3] = a.udata[0] shl (b and 31)
|
||||
|
||||
proc `+`*(a, b: Int128): Int128 =
|
||||
proc `+`*(a,b: Int128): Int128 =
|
||||
let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
|
||||
result.udata[0] = cast[uint32](tmp0)
|
||||
let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
|
||||
@@ -304,7 +305,7 @@ proc `-`*(a: Int128): Int128 =
|
||||
result = bitnot(a)
|
||||
result.inc
|
||||
|
||||
proc `-`*(a, b: Int128): Int128 =
|
||||
proc `-`*(a,b: Int128): Int128 =
|
||||
a + (-b)
|
||||
|
||||
proc `-=`*(a: var Int128, b: Int128) =
|
||||
@@ -341,7 +342,7 @@ proc `*`*(a: Int128, b: int32): Int128 =
|
||||
proc `*=`*(a: var Int128, b: int32): Int128 =
|
||||
result = result * b
|
||||
|
||||
proc makeInt128(high, low: uint64): Int128 =
|
||||
proc makeInt128(high,low: uint64): Int128 =
|
||||
result.udata[0] = cast[uint32](low)
|
||||
result.udata[1] = cast[uint32](low shr 32)
|
||||
result.udata[2] = cast[uint32](high)
|
||||
@@ -353,7 +354,7 @@ proc high64(a: Int128): uint64 =
|
||||
proc low64(a: Int128): uint64 =
|
||||
bitconcat(a.udata[1], a.udata[0])
|
||||
|
||||
proc `*`*(lhs, rhs: Int128): Int128 =
|
||||
proc `*`*(lhs,rhs: Int128): Int128 =
|
||||
let
|
||||
a = cast[uint64](lhs.udata[0])
|
||||
b = cast[uint64](lhs.udata[1])
|
||||
@@ -372,8 +373,8 @@ proc `*`*(lhs, rhs: Int128): Int128 =
|
||||
let b00 = cast[uint64](rhs.udata[0])
|
||||
|
||||
result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
|
||||
result += toInt128(a32 * b00) shl 32
|
||||
result += toInt128(a00 * b32) shl 32
|
||||
result = result + toInt128(a32 * b00) shl 32
|
||||
result = result + toInt128(a00 * b32) shl 32
|
||||
|
||||
proc `*=`*(a: var Int128, b: Int128) =
|
||||
a = a * b
|
||||
@@ -388,7 +389,7 @@ proc fastLog2*(a: Int128): int =
|
||||
if a.udata[1] != 0:
|
||||
return 32 + fastLog2(a.udata[1])
|
||||
if a.udata[0] != 0:
|
||||
return fastLog2(a.udata[0])
|
||||
return fastLog2(a.udata[0])
|
||||
|
||||
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
assert(divisor != Zero)
|
||||
@@ -423,10 +424,10 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
|
||||
# Uses shift-subtract algorithm to divide dividend by denominator. The
|
||||
# remainder will be left in dividend.
|
||||
for i in 0..shift:
|
||||
for i in 0 .. shift:
|
||||
quotient = quotient shl 1
|
||||
if dividend >= denominator:
|
||||
dividend -= denominator
|
||||
dividend = dividend - denominator
|
||||
quotient = bitor(quotient, One)
|
||||
|
||||
denominator = denominator shr 1
|
||||
@@ -440,12 +441,12 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
else:
|
||||
result.remainder = dividend
|
||||
|
||||
proc `div`*(a, b: Int128): Int128 =
|
||||
let (a, b) = divMod(a, b)
|
||||
proc `div`*(a,b: Int128): Int128 =
|
||||
let (a,b) = divMod(a,b)
|
||||
return a
|
||||
|
||||
proc `mod`*(a, b: Int128): Int128 =
|
||||
let (a, b) = divMod(a, b)
|
||||
proc `mod`*(a,b: Int128): Int128 =
|
||||
let (a,b) = divMod(a,b)
|
||||
return b
|
||||
|
||||
proc addInt128*(result: var string; value: Int128) =
|
||||
@@ -476,7 +477,7 @@ proc `$`*(a: Int128): string =
|
||||
|
||||
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
assert(pos < arg.len)
|
||||
assert(arg[pos] in {'-', '0'..'9'})
|
||||
assert(arg[pos] in {'-','0'..'9'})
|
||||
|
||||
var isNegative = false
|
||||
var pos = pos
|
||||
@@ -485,7 +486,7 @@ proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
pos += 1
|
||||
|
||||
result = Zero
|
||||
while pos < arg.len and arg[pos] in '0'..'9':
|
||||
while pos < arg.len and arg[pos] in '0' .. '9':
|
||||
result = result * Ten
|
||||
result.inc(uint32(arg[pos]) - uint32('0'))
|
||||
pos += 1
|
||||
@@ -496,13 +497,13 @@ proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
# fluff
|
||||
|
||||
proc `<`*(a: Int128, b: BiggestInt): bool =
|
||||
cmp(a, toInt128(b)) < 0
|
||||
cmp(a,toInt128(b)) < 0
|
||||
|
||||
proc `<`*(a: BiggestInt, b: Int128): bool =
|
||||
cmp(toInt128(a), b) < 0
|
||||
|
||||
proc `<=`*(a: Int128, b: BiggestInt): bool =
|
||||
cmp(a, toInt128(b)) <= 0
|
||||
cmp(a,toInt128(b)) <= 0
|
||||
|
||||
proc `<=`*(a: BiggestInt, b: Int128): bool =
|
||||
cmp(toInt128(a), b) <= 0
|
||||
@@ -538,7 +539,7 @@ proc toFloat64*(arg: Int128): float64 =
|
||||
|
||||
proc ldexp(x: float64, exp: cint): float64 {.importc: "ldexp", header: "<math.h>".}
|
||||
|
||||
template bitor(a, b, c: Int128): Int128 = bitor(bitor(a, b), c)
|
||||
template bitor(a,b,c: Int128): Int128 = bitor(bitor(a,b), c)
|
||||
|
||||
proc toInt128*(arg: float64): Int128 =
|
||||
let isNegative = arg < 0
|
||||
@@ -591,3 +592,107 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
|
||||
return maskUInt64(arg)
|
||||
else:
|
||||
assert(false, "masking only implemented for 1, 2, 4 and 8 bytes")
|
||||
|
||||
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let (a,b) = divMod(Ten,Ten)
|
||||
|
||||
doAssert $One == "1"
|
||||
doAssert $Ten == "10"
|
||||
doAssert $Zero == "0"
|
||||
let c = parseDecimalInt128("12345678989876543210123456789")
|
||||
doAssert $c == "12345678989876543210123456789"
|
||||
|
||||
var d : array[39, Int128]
|
||||
d[0] = parseDecimalInt128("1")
|
||||
d[1] = parseDecimalInt128("10")
|
||||
d[2] = parseDecimalInt128("100")
|
||||
d[3] = parseDecimalInt128("1000")
|
||||
d[4] = parseDecimalInt128("10000")
|
||||
d[5] = parseDecimalInt128("100000")
|
||||
d[6] = parseDecimalInt128("1000000")
|
||||
d[7] = parseDecimalInt128("10000000")
|
||||
d[8] = parseDecimalInt128("100000000")
|
||||
d[9] = parseDecimalInt128("1000000000")
|
||||
d[10] = parseDecimalInt128("10000000000")
|
||||
d[11] = parseDecimalInt128("100000000000")
|
||||
d[12] = parseDecimalInt128("1000000000000")
|
||||
d[13] = parseDecimalInt128("10000000000000")
|
||||
d[14] = parseDecimalInt128("100000000000000")
|
||||
d[15] = parseDecimalInt128("1000000000000000")
|
||||
d[16] = parseDecimalInt128("10000000000000000")
|
||||
d[17] = parseDecimalInt128("100000000000000000")
|
||||
d[18] = parseDecimalInt128("1000000000000000000")
|
||||
d[19] = parseDecimalInt128("10000000000000000000")
|
||||
d[20] = parseDecimalInt128("100000000000000000000")
|
||||
d[21] = parseDecimalInt128("1000000000000000000000")
|
||||
d[22] = parseDecimalInt128("10000000000000000000000")
|
||||
d[23] = parseDecimalInt128("100000000000000000000000")
|
||||
d[24] = parseDecimalInt128("1000000000000000000000000")
|
||||
d[25] = parseDecimalInt128("10000000000000000000000000")
|
||||
d[26] = parseDecimalInt128("100000000000000000000000000")
|
||||
d[27] = parseDecimalInt128("1000000000000000000000000000")
|
||||
d[28] = parseDecimalInt128("10000000000000000000000000000")
|
||||
d[29] = parseDecimalInt128("100000000000000000000000000000")
|
||||
d[30] = parseDecimalInt128("1000000000000000000000000000000")
|
||||
d[31] = parseDecimalInt128("10000000000000000000000000000000")
|
||||
d[32] = parseDecimalInt128("100000000000000000000000000000000")
|
||||
d[33] = parseDecimalInt128("1000000000000000000000000000000000")
|
||||
d[34] = parseDecimalInt128("10000000000000000000000000000000000")
|
||||
d[35] = parseDecimalInt128("100000000000000000000000000000000000")
|
||||
d[36] = parseDecimalInt128("1000000000000000000000000000000000000")
|
||||
d[37] = parseDecimalInt128("10000000000000000000000000000000000000")
|
||||
d[38] = parseDecimalInt128("100000000000000000000000000000000000000")
|
||||
|
||||
for i in 0 ..< d.len:
|
||||
for j in 0 ..< d.len:
|
||||
doAssert(cmp(d[i], d[j]) == cmp(i,j))
|
||||
if i + j < d.len:
|
||||
doAssert d[i] * d[j] == d[i+j]
|
||||
if i - j >= 0:
|
||||
doAssert d[i] div d[j] == d[i-j]
|
||||
|
||||
var sum: Int128
|
||||
|
||||
for it in d:
|
||||
sum += it
|
||||
|
||||
doAssert $sum == "111111111111111111111111111111111111111"
|
||||
|
||||
for it in d.mitems:
|
||||
it = -it
|
||||
|
||||
for i in 0 ..< d.len:
|
||||
for j in 0 ..< d.len:
|
||||
doAssert(cmp(d[i], d[j]) == -cmp(i,j))
|
||||
if i + j < d.len:
|
||||
doAssert d[i] * d[j] == -d[i+j]
|
||||
if i - j >= 0:
|
||||
doAssert d[i] div d[j] == -d[i-j]
|
||||
|
||||
doAssert $high(Int128) == "170141183460469231731687303715884105727"
|
||||
doAssert $low(Int128) == "-170141183460469231731687303715884105728"
|
||||
|
||||
var ma = 100'i64
|
||||
var mb = 13
|
||||
|
||||
doAssert toInt128(ma) * toInt128(0) == toInt128(0)
|
||||
doAssert toInt128(-ma) * toInt128(0) == toInt128(0)
|
||||
|
||||
# sign correctness
|
||||
doAssert divMod(toInt128( ma),toInt128( mb)) == (toInt128( ma div mb), toInt128( ma mod mb))
|
||||
doAssert divMod(toInt128(-ma),toInt128( mb)) == (toInt128(-ma div mb), toInt128(-ma mod mb))
|
||||
doAssert divMod(toInt128( ma),toInt128(-mb)) == (toInt128( ma div -mb), toInt128( ma mod -mb))
|
||||
doAssert divMod(toInt128(-ma),toInt128(-mb)) == (toInt128(-ma div -mb), toInt128(-ma mod -mb))
|
||||
|
||||
doAssert divMod(toInt128( mb),toInt128( mb)) == (toInt128( mb div mb), toInt128( mb mod mb))
|
||||
doAssert divMod(toInt128(-mb),toInt128( mb)) == (toInt128(-mb div mb), toInt128(-mb mod mb))
|
||||
doAssert divMod(toInt128( mb),toInt128(-mb)) == (toInt128( mb div -mb), toInt128( mb mod -mb))
|
||||
doAssert divMod(toInt128(-mb),toInt128(-mb)) == (toInt128(-mb div -mb), toInt128(-mb mod -mb))
|
||||
|
||||
doAssert divMod(toInt128( mb),toInt128( ma)) == (toInt128( mb div ma), toInt128( mb mod ma))
|
||||
doAssert divMod(toInt128(-mb),toInt128( ma)) == (toInt128(-mb div ma), toInt128(-mb mod ma))
|
||||
doAssert divMod(toInt128( mb),toInt128(-ma)) == (toInt128( mb div -ma), toInt128( mb mod -ma))
|
||||
doAssert divMod(toInt128(-mb),toInt128(-ma)) == (toInt128(-mb div -ma), toInt128(-mb mod -ma))
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Implementation of the check that `recover` needs, see
|
||||
## https://github.com/nim-lang/RFCs/issues/244 for more details.
|
||||
|
||||
import
|
||||
ast, types, renderer, intsets
|
||||
|
||||
proc canAlias(arg, ret: PType; marker: var IntSet): bool
|
||||
|
||||
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
result = canAliasN(arg, n[i], marker)
|
||||
if result: return
|
||||
of nkRecCase:
|
||||
assert(n[0].kind == nkSym)
|
||||
result = canAliasN(arg, n[0], marker)
|
||||
if result: return
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = canAliasN(arg, lastSon(n[i]), marker)
|
||||
if result: return
|
||||
else: discard
|
||||
of nkSym:
|
||||
result = canAlias(arg, n.sym.typ, marker)
|
||||
else: discard
|
||||
|
||||
proc canAlias(arg, ret: PType; marker: var IntSet): bool =
|
||||
if containsOrIncl(marker, ret.id):
|
||||
return false
|
||||
|
||||
if ret.kind in {tyPtr, tyPointer}:
|
||||
# unsafe so we don't care:
|
||||
return false
|
||||
if compareTypes(arg, ret, dcEqIgnoreDistinct):
|
||||
return true
|
||||
case ret.kind
|
||||
of tyObject:
|
||||
if isFinal(ret):
|
||||
result = canAliasN(arg, ret.n, marker)
|
||||
if not result and ret.len > 0 and ret[0] != nil:
|
||||
result = canAlias(arg, ret[0], marker)
|
||||
else:
|
||||
result = true
|
||||
of tyTuple:
|
||||
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.lastSon, marker)
|
||||
of tyProc:
|
||||
result = ret.callConv == ccClosure
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc isValueOnlyType(t: PType): bool =
|
||||
# t doesn't contain pointers and references
|
||||
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
|
||||
result = not types.searchTypeFor(t, wrap)
|
||||
|
||||
proc canAlias*(arg, ret: PType): bool =
|
||||
if isValueOnlyType(arg):
|
||||
# can alias only with unsafeAddr(arg.x) and we don't care if it is not safe
|
||||
result = false
|
||||
else:
|
||||
var marker = initIntSet()
|
||||
result = canAlias(arg, ret, marker)
|
||||
|
||||
proc containsVariable(n: PNode): bool =
|
||||
case n.kind
|
||||
of nodesToIgnoreSet:
|
||||
result = false
|
||||
of nkSym:
|
||||
result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
|
||||
else:
|
||||
for ch in n:
|
||||
if containsVariable(ch): return true
|
||||
result = false
|
||||
|
||||
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 exisiting inheritance and closure restrictions.
|
||||
case n.kind
|
||||
of nkCharLit..nkNilLit:
|
||||
result = true
|
||||
of nkCallKinds:
|
||||
# XXX: as long as we don't update the analysis while examining arguments
|
||||
# we can do an early check of the return type, otherwise this is a
|
||||
# bug and needs to be moved below
|
||||
if tfNoSideEffect notin n[0].typ.flags:
|
||||
return false
|
||||
for i in 1..<n.len:
|
||||
if checkIsolate(n[i]):
|
||||
discard "fine, it is isolated already"
|
||||
else:
|
||||
let argType = n[i].typ
|
||||
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
|
||||
# that materialized which is only possible if a variable that contains a `ref`
|
||||
# is involved.
|
||||
return false
|
||||
result = true
|
||||
of nkIfStmt, nkIfExpr:
|
||||
for it in n:
|
||||
result = checkIsolate(it.lastSon)
|
||||
if not result: break
|
||||
of nkCaseStmt:
|
||||
for i in 1..<n.len:
|
||||
result = checkIsolate(n[i].lastSon)
|
||||
if not result: break
|
||||
of nkObjConstr:
|
||||
result = true
|
||||
for i in 1..<n.len:
|
||||
result = checkIsolate(n[i].lastSon)
|
||||
if not result: break
|
||||
of nkBracket, nkTupleConstr, nkPar:
|
||||
for it in n:
|
||||
result = checkIsolate(it)
|
||||
if not result: break
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
|
||||
result = checkIsolate(n[1])
|
||||
of nkObjUpConv, nkObjDownConv, nkDotExpr:
|
||||
result = checkIsolate(n[0])
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
if n.len > 0:
|
||||
result = checkIsolate(n[^1])
|
||||
else:
|
||||
result = false
|
||||
else:
|
||||
# unanalysable expression:
|
||||
result = false
|
||||
else:
|
||||
# no ref, no cry:
|
||||
result = true
|
||||
1297
compiler/jsgen.nim
1297
compiler/jsgen.nim
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user