mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 19:03:42 +00:00
Compare commits
224 Commits
pr_private
...
v0.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88a0edba4b | ||
|
|
5a12553394 | ||
|
|
7469f5fa78 | ||
|
|
6bd301ca9c | ||
|
|
0047f7cf4f | ||
|
|
8be734bf46 | ||
|
|
9b0cb52d74 | ||
|
|
1aefa74e46 | ||
|
|
c0085bc2b7 | ||
|
|
43cba52ed8 | ||
|
|
d6d5dd30e9 | ||
|
|
073651237a | ||
|
|
f23ca7f539 | ||
|
|
2bf1985f1a | ||
|
|
abebc395d3 | ||
|
|
ac41aec348 | ||
|
|
575f688545 | ||
|
|
95fd0d0a6f | ||
|
|
c4cbc5f449 | ||
|
|
d5d661b70e | ||
|
|
46cb304770 | ||
|
|
b4742cc496 | ||
|
|
abd4330dd0 | ||
|
|
876d0aaade | ||
|
|
8b6ea5d451 | ||
|
|
4771b08ab5 | ||
|
|
a59efc53f2 | ||
|
|
ae04734031 | ||
|
|
75f0a7a7af | ||
|
|
d91b4b1bb7 | ||
|
|
5a11ae85b1 | ||
|
|
8eb66f6c6b | ||
|
|
6f214131a2 | ||
|
|
d2f9a40b3c | ||
|
|
622eadff52 | ||
|
|
93b58ebe1b | ||
|
|
f7b061f597 | ||
|
|
e34a318b9f | ||
|
|
641d1e1175 | ||
|
|
07505d87e2 | ||
|
|
6ffe82a5aa | ||
|
|
2b4a7ff6f6 | ||
|
|
3d33e96388 | ||
|
|
19225b71ab | ||
|
|
e5e2b067f4 | ||
|
|
3866e3302a | ||
|
|
607ce16acf | ||
|
|
694d57c899 | ||
|
|
ed627cff3a | ||
|
|
8a5860d196 | ||
|
|
b5ab4dabe2 | ||
|
|
b3b10ec849 | ||
|
|
6ec566cdd8 | ||
|
|
c4e26f7400 | ||
|
|
ab98141c75 | ||
|
|
14c6e3a6ab | ||
|
|
808746e864 | ||
|
|
386f126dd7 | ||
|
|
ce9eaee303 | ||
|
|
7c7bab0626 | ||
|
|
da2a149522 | ||
|
|
8afe92a1ab | ||
|
|
2a0c9dc063 | ||
|
|
e0b4d034bc | ||
|
|
12baecb42d | ||
|
|
a144bc96b9 | ||
|
|
e0991c4bcf | ||
|
|
40c2f504fb | ||
|
|
80e51bb407 | ||
|
|
9b217c10e6 | ||
|
|
78b665cc7f | ||
|
|
092154de02 | ||
|
|
0646d6a720 | ||
|
|
585a08548a | ||
|
|
f77bc2522f | ||
|
|
5cc49aad44 | ||
|
|
e3c6f4d21b | ||
|
|
615be176e6 | ||
|
|
5dfec61597 | ||
|
|
df32d891a9 | ||
|
|
c96e6682a0 | ||
|
|
753f8a05d2 | ||
|
|
00ccdec238 | ||
|
|
49de021b1a | ||
|
|
5ecfbb5a4a | ||
|
|
1b10f926c8 | ||
|
|
a31b3ef3cf | ||
|
|
8501971a60 | ||
|
|
5863461d47 | ||
|
|
cb3a920097 | ||
|
|
88896194bd | ||
|
|
5689003410 | ||
|
|
789c8e0065 | ||
|
|
e74e85a41e | ||
|
|
2ee6d209c2 | ||
|
|
3e8c4519d4 | ||
|
|
cbdba3ceb3 | ||
|
|
a9322ed873 | ||
|
|
66660eca32 | ||
|
|
d13c75eb2f | ||
|
|
f70a0c90f1 | ||
|
|
6a23772e23 | ||
|
|
da3a7b852d | ||
|
|
7ab44997aa | ||
|
|
b94e7b65a0 | ||
|
|
20f6acc0da | ||
|
|
b0e86d0f05 | ||
|
|
6a6a9423e4 | ||
|
|
191b7e0bde | ||
|
|
1e48169bbc | ||
|
|
bd35d44808 | ||
|
|
d1de6b9fe4 | ||
|
|
466d5ba060 | ||
|
|
7938947907 | ||
|
|
476b3b1c13 | ||
|
|
59fbd261a6 | ||
|
|
eb295bbb7d | ||
|
|
e6c963259c | ||
|
|
c454cdac49 | ||
|
|
09d43c1d8f | ||
|
|
281baadb33 | ||
|
|
d7ea8588c5 | ||
|
|
10fa252426 | ||
|
|
b228b741a3 | ||
|
|
c2d0e0f7d5 | ||
|
|
95df6af527 | ||
|
|
199aa1f3a1 | ||
|
|
57df2c9c3a | ||
|
|
17cac707d0 | ||
|
|
970c7616bc | ||
|
|
1503970c3b | ||
|
|
922a4ed195 | ||
|
|
c39a799aec | ||
|
|
62691c5d73 | ||
|
|
ab5bd9e6ff | ||
|
|
3c3f07cfde | ||
|
|
843ae0073b | ||
|
|
d206e881e9 | ||
|
|
712d9ce1b9 | ||
|
|
ba6eb0151c | ||
|
|
28708e0b8e | ||
|
|
e08cf5a947 | ||
|
|
38aa7f6d20 | ||
|
|
763357089e | ||
|
|
6576c1ab5c | ||
|
|
8a16fb972b | ||
|
|
f1b2182035 | ||
|
|
50358e6d4a | ||
|
|
48770b04bd | ||
|
|
7e702d95c1 | ||
|
|
8396ddb3a4 | ||
|
|
8ab0be4530 | ||
|
|
30d7cf02ad | ||
|
|
98b9ffa3a4 | ||
|
|
a5a62b562f | ||
|
|
06904a212a | ||
|
|
a74104eda5 | ||
|
|
f8087a2815 | ||
|
|
6a7b490046 | ||
|
|
ceafb7f771 | ||
|
|
433dfaf288 | ||
|
|
4d18ee498a | ||
|
|
ef018d7a63 | ||
|
|
2a36cb7fc9 | ||
|
|
810166ca95 | ||
|
|
10407fefec | ||
|
|
d5ea497cff | ||
|
|
c6ab69b84e | ||
|
|
edc151fa64 | ||
|
|
6d977e66cb | ||
|
|
f3ae2dcfa3 | ||
|
|
cd49bba7b3 | ||
|
|
194eebd4b8 | ||
|
|
b41d31e887 | ||
|
|
cb4bb4d6c7 | ||
|
|
cd35f9664c | ||
|
|
0ba1624325 | ||
|
|
6ad45a1c97 | ||
|
|
5cf9cbe9a5 | ||
|
|
9789146899 | ||
|
|
905fc43abe | ||
|
|
aacc97e854 | ||
|
|
e0b41736b3 | ||
|
|
8d46524b1b | ||
|
|
3aa034f40d | ||
|
|
eb9ac127b9 | ||
|
|
22c515b067 | ||
|
|
4c7dec34d5 | ||
|
|
81f15184e5 | ||
|
|
368e12ed36 | ||
|
|
c57485e67b | ||
|
|
2d606e38ea | ||
|
|
c1097ea949 | ||
|
|
4144893ba2 | ||
|
|
dacd1d071a | ||
|
|
1348cb525b | ||
|
|
77166176b2 | ||
|
|
e15ce6bdeb | ||
|
|
464142683f | ||
|
|
7095a3dfa4 | ||
|
|
7e235779f0 | ||
|
|
b68ee27dd5 | ||
|
|
63116d94ae | ||
|
|
01fcde2146 | ||
|
|
ea4d72a379 | ||
|
|
78c6f01b90 | ||
|
|
41e0fc593c | ||
|
|
21910d6eb1 | ||
|
|
7bfde5fa40 | ||
|
|
9e28b3ccc9 | ||
|
|
b4464f1dfa | ||
|
|
4f69f7b62d | ||
|
|
6530a4100a | ||
|
|
9797da3615 | ||
|
|
0af9085e78 | ||
|
|
e1c29d7a19 | ||
|
|
32cecf3a42 | ||
|
|
05d647398a | ||
|
|
4c8a02165e | ||
|
|
7e6c3f3684 | ||
|
|
65a2b3bb6f | ||
|
|
8dfebb3282 | ||
|
|
de2c271293 | ||
|
|
022cd2c144 |
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,9 +0,0 @@
|
||||
# Avoids changelog conflicts by assuming additions-only, which is by far the common case.
|
||||
# In the rare case where branch b1 rebases against branch b2 and both branches
|
||||
# modified the same changelog entry, you'll end up with that changelog entry
|
||||
# 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
|
||||
|
||||
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Have you found an unexpected behavior? Use this template.
|
||||
---
|
||||
|
||||
<!-- 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!"
|
||||
```
|
||||
|
||||
|
||||
### Current Output
|
||||
```
|
||||
Hola mundo!
|
||||
```
|
||||
|
||||
|
||||
### Expected Output
|
||||
<!-- What should be the correct output? -->
|
||||
```
|
||||
Hello World!
|
||||
```
|
||||
|
||||
|
||||
### Possible Solution
|
||||
<!--- Have you found a possible solution? Post it here. -->
|
||||
|
||||
|
||||
### Additional Information
|
||||
<!--- 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
|
||||
```
|
||||
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,73 +0,0 @@
|
||||
name: "Bug Report"
|
||||
description: "Create a new bug report. Have you found an unexpected behavior? Use this form."
|
||||
title: "Think about the title, twice."
|
||||
labels: ["unconfirmed"]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please provide a minimal code example that reproduces the bug if possible.
|
||||
Reports with a reproducible example or detailed information will likely receive fixes faster.
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Describe the problem. Code example can be given here.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: nim-version
|
||||
attributes:
|
||||
label: Nim Version
|
||||
description: |
|
||||
Can be obtained from `nim -v` on the command line along with the OS/architecture.
|
||||
For development versions, make sure to include the commit hash.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current-logs
|
||||
attributes:
|
||||
label: Current Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
id: expected-logs
|
||||
attributes:
|
||||
label: Expected Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
id: known-workarounds
|
||||
attributes:
|
||||
label: Known Workarounds
|
||||
description: Provide any known workarounds.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: extra-info
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Any additional relevant information.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
|
||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
||||
- If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
|
||||
- If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
|
||||
The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
|
||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
||||
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.
|
||||
32
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Do you want to suggest a new feature? Use this template.
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Notice there is now a separate repository for the detailed RFCs and proposals:
|
||||
https://github.com/nim-lang/RFCs
|
||||
|
||||
If you have a simple feature request, you can post it here using this template,
|
||||
but bear in mind that adding new features to the language is currently a low priority.
|
||||
-->
|
||||
|
||||
|
||||
### Summary
|
||||
<!--- Short summary of your proposed feature -->
|
||||
|
||||
|
||||
### Description
|
||||
<!--- Describe your solution, what problem does it fix? -->
|
||||
|
||||
|
||||
### Alternatives
|
||||
<!--- Are there any alternatives you've considered? -->
|
||||
|
||||
|
||||
### Additional Information
|
||||
<!--- For Example:
|
||||
* A link to a project where the issue is relevant.
|
||||
* A link to a related issue or discussion.
|
||||
-->
|
||||
72
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
72
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: "Feature request"
|
||||
description: "Create a new Feature Request. Do you want to suggest a new feature? Use this form."
|
||||
title: "Think about the title, twice."
|
||||
labels: ["unconfirmed"]
|
||||
body:
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- Please provide a minimal code example that illustrates the basic idea behind your feature request.
|
||||
Reports with full repro code and descriptive detailed information will likely receive feedback faster.
|
||||
- There is a separate repository for the detailed RFCs and proposals: https://github.com/nim-lang/RFCs.
|
||||
If you have a simple feature request, you can post it here using this form,
|
||||
but bear in mind that adding new features to the language is currently a low priority.
|
||||
|
||||
- type: textarea
|
||||
id: summary
|
||||
attributes:
|
||||
label: Summary
|
||||
description: |
|
||||
Use DETAILED DESCRIPTIVE information about the feature request.
|
||||
Here, you go into more details about your ideas. This section can be a few paragraphs long.
|
||||
placeholder: Short summary of your proposed feature.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe your solution, what problem does it fix?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: Are there any alternatives you've considered?
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: Examples
|
||||
attributes:
|
||||
label: Examples
|
||||
description: Provide examples for your feature request here.
|
||||
placeholder: Example code here.
|
||||
|
||||
- type: textarea
|
||||
id: incompatibility
|
||||
attributes:
|
||||
label: Backwards Compatibility
|
||||
description: If your Feature Request introduces backward-incompatible changes, describe them and propose how to deal with them.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: links
|
||||
attributes:
|
||||
label: Links
|
||||
description: Please copy and paste any relevant links to projects, issues, discussions, technical documentations, code samples, etc.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- Thanks for your contributions!, your ideas will receive feedback from the community soon...
|
||||
- **Remember to :star: Star the Nim project on GitHub!.**
|
||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
||||
31
.github/workflows/bisects.yml
vendored
31
.github/workflows/bisects.yml
vendored
@@ -1,31 +0,0 @@
|
||||
# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901
|
||||
name: issue comments bisects
|
||||
on:
|
||||
issue_comment:
|
||||
types: created
|
||||
|
||||
jobs:
|
||||
bisects:
|
||||
if: |
|
||||
github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest, windows-latest, macos-latest]
|
||||
name: ${{ matrix.platform }}-bisects
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: 'devel'
|
||||
|
||||
- uses: juancarlospaco/nimrun-action@nim
|
||||
if: |
|
||||
runner.os == 'Linux' && contains(github.event.comment.body, '-d:linux' ) ||
|
||||
runner.os == 'Windows' && contains(github.event.comment.body, '-d:windows') ||
|
||||
runner.os == 'macOS' && contains(github.event.comment.body, '-d:osx' ) ||
|
||||
runner.os == 'Linux' && !contains(github.event.comment.body, '-d:linux') && !contains(github.event.comment.body, '-d:windows') && !contains(github.event.comment.body, '-d:osx')
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
116
.github/workflows/ci_docs.yml
vendored
116
.github/workflows/ci_docs.yml
vendored
@@ -1,116 +0,0 @@
|
||||
name: Nim Docs CI
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'compiler/**.nim'
|
||||
- 'config/nimdoc.cfg'
|
||||
- 'doc/**.rst'
|
||||
- 'doc/**.md'
|
||||
- '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/**.nim'
|
||||
- 'config/nimdoc.cfg'
|
||||
- 'doc/**.rst'
|
||||
- 'doc/**.md'
|
||||
- 'doc/nimdoc.css'
|
||||
- 'lib/**.nim'
|
||||
- 'nimdoc/testproject/expected/testproject.html'
|
||||
- 'tools/dochack/dochack.nim'
|
||||
- 'tools/kochdocs.nim'
|
||||
- '.github/workflows/ci_docs.yml'
|
||||
- 'koch.nim'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
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-13
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
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@v4
|
||||
with:
|
||||
build_dir: doc/html
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
76
.github/workflows/ci_gcc14.yml
vendored
76
.github/workflows/ci_gcc14.yml
vendored
@@ -1,76 +0,0 @@
|
||||
name: GCC 14
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt remove needrestart
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt 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 (Linux amd64 gcc 14)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update -qq
|
||||
sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
- 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: 'NIM_TESTAMENT_DISABLE_SSL'
|
||||
shell: bash
|
||||
run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
|
||||
|
||||
- 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
|
||||
75
.github/workflows/ci_packages.yml
vendored
75
.github/workflows/ci_packages.yml
vendored
@@ -1,75 +0,0 @@
|
||||
name: Packages CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
- 'version-2-0'
|
||||
- 'version-1-6'
|
||||
- 'version-1-2'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-13]
|
||||
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@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.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
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- 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
|
||||
90
.github/workflows/ci_publish.yml
vendored
90
.github/workflows/ci_publish.yml
vendored
@@ -1,90 +0,0 @@
|
||||
name: Tracking orc-booting compiler memory usage
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event.pull_request.merged == true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.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: 'Build koch'
|
||||
shell: bash
|
||||
run: nim c koch
|
||||
|
||||
- name: 'Build Nim'
|
||||
shell: bash
|
||||
run: ./koch boot -d:release -d:nimStrictMode --lib:lib
|
||||
|
||||
- name: 'Action'
|
||||
shell: bash
|
||||
run: nim c -r -d:release ci/action.nim
|
||||
|
||||
- name: 'Comment'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
try {
|
||||
const data = fs.readFileSync('ci/nimcache/results.txt', 'utf8');
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: data
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
25
.github/workflows/stale.yml
vendored
25
.github/workflows/stale.yml
vendored
@@ -1,25 +0,0 @@
|
||||
# https://github.com/actions/stale#usage
|
||||
name: Stale pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Midnight.
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-pr-stale: 365
|
||||
days-before-pr-close: 30
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
exempt-pr-labels: "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]"
|
||||
exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority"
|
||||
stale-pr-message: >
|
||||
This pull request is stale because it has been open for 1 year with no activity.
|
||||
Contribute more commits on the pull request and rebase it on the latest devel,
|
||||
or it will be closed in 30 days. Thank you for your contributions.
|
||||
close-pr-message: >
|
||||
This pull request has been marked as stale and closed due to inactivity after 395 days.
|
||||
42
.gitignore
vendored
42
.gitignore
vendored
@@ -3,9 +3,9 @@
|
||||
!*.*
|
||||
|
||||
# Cache
|
||||
nimcache*/
|
||||
rnimcache*/
|
||||
dnimcache*/
|
||||
nimcache/
|
||||
rnimcache/
|
||||
dnimcache/
|
||||
|
||||
*.o
|
||||
!/icons/*.o
|
||||
@@ -30,7 +30,7 @@ deinstall.sh
|
||||
|
||||
doc/html/
|
||||
doc/*.html
|
||||
doc/pdf
|
||||
doc/*.pdf
|
||||
doc/*.idx
|
||||
/web/upload
|
||||
/build/*
|
||||
@@ -64,51 +64,23 @@ lib/**/*.html
|
||||
testament.db
|
||||
/tests/**/*.json
|
||||
/tests/**/*.js
|
||||
|
||||
/csources
|
||||
/csources_v1
|
||||
/csources_v2
|
||||
|
||||
/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/*
|
||||
pkgs/
|
||||
/compiler/compiler/
|
||||
|
||||
60
.gitlab-ci.yml
Normal file
60
.gitlab-ci.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
image: ubuntu:16.04
|
||||
|
||||
stages:
|
||||
- pre-build
|
||||
- build
|
||||
- deploy
|
||||
- test
|
||||
|
||||
.linux_set_path: &linux_set_path_def
|
||||
before_script:
|
||||
- export PATH=$(pwd)/bin${PATH:+:$PATH}
|
||||
tags:
|
||||
- linux
|
||||
|
||||
.windows_set_path: &win_set_path_def
|
||||
before_script:
|
||||
- set PATH=%CD%\bin;%PATH%
|
||||
tags:
|
||||
- windows
|
||||
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
script:
|
||||
- ci\build.bat
|
||||
artifacts:
|
||||
paths:
|
||||
- bin\nim.exe
|
||||
- bin\nimd.exe
|
||||
- compiler\nim.exe
|
||||
- koch.exe
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- windows
|
||||
|
||||
deploy-windows:
|
||||
stage: deploy
|
||||
script:
|
||||
- koch.exe winrelease
|
||||
artifacts:
|
||||
paths:
|
||||
- build/*.exe
|
||||
- build/*.zip
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
|
||||
|
||||
|
||||
test-windows:
|
||||
stage: test
|
||||
<<: *win_set_path_def
|
||||
script:
|
||||
- call ci\deps.bat
|
||||
- nim c testament\tester
|
||||
- testament\tester.exe --pedantic all
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
92
.travis.yml
Normal file
92
.travis.yml
Normal file
@@ -0,0 +1,92 @@
|
||||
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
|
||||
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
|
||||
39
appveyor.yml
Normal file
39
appveyor.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
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%'
|
||||
|
||||
matrix:
|
||||
#allow_failures:
|
||||
# - NIM_TEST_PACKAGES: true
|
||||
fast_finish: true
|
||||
|
||||
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
|
||||
@@ -1,169 +0,0 @@
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- 'devel'
|
||||
- 'version-*'
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
variables:
|
||||
- name: skipci
|
||||
value: false
|
||||
|
||||
jobs:
|
||||
- job: packages
|
||||
|
||||
timeoutInMinutes: 90 # default `60` led to lots of cancelled jobs; use 0 for unlimited (may be undesirable)
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
# Linux_i386:
|
||||
# # on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
|
||||
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-13'
|
||||
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"
|
||||
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
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'))
|
||||
|
||||
- checkout: self
|
||||
fetchDepth: 2 # see D20210329T004830
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
if nimIsCiSkip; then
|
||||
echo '##vso[task.setvariable variable=skipci]true'
|
||||
fi
|
||||
displayName: 'Check whether to skip CI'
|
||||
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '20.x'
|
||||
displayName: 'Install node.js 20.x'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
|
||||
- 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'))
|
||||
|
||||
- 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.
|
||||
|
||||
cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
|
||||
Package: libllvm6.0
|
||||
Pin: origin "azure.archive.ubuntu.com"
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# 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
|
||||
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
|
||||
exec $(which gcc) -m32 "\$@"
|
||||
EOF
|
||||
|
||||
cat << EOF > bin/g++
|
||||
#!/bin/bash
|
||||
|
||||
exec $(which g++) -m32 "\$@"
|
||||
EOF
|
||||
|
||||
echo_run chmod 755 bin/gcc
|
||||
echo_run chmod 755 bin/g++
|
||||
|
||||
displayName: 'Install dependencies (i386 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386'))
|
||||
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
||||
# XXX can't find boehm on macos 13
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
||||
|
||||
displayName: 'Install dependencies (Windows)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
|
||||
- bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||
displayName: 'Add build binaries to PATH'
|
||||
|
||||
- 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'
|
||||
|
||||
# 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'
|
||||
|
||||
- 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)
|
||||
14
bin/nim-gdb
14
bin/nim-gdb
@@ -5,16 +5,10 @@ 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
|
||||
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py"
|
||||
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
|
||||
# Set the environment variable `NIM_GDB` to overwrite the call to a
|
||||
@@ -22,4 +16,6 @@ GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/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\debug\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 +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%
|
||||
)
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
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.
|
||||
|
||||
37
changelog.md
37
changelog.md
@@ -1,28 +1,37 @@
|
||||
# v2.x.x - yyyy-mm-dd
|
||||
# v1.1 - XXXX-XX-XX
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
|
||||
floating point numbers that are minimal in size and possess round-trip and correct
|
||||
rounding guarantees (via the
|
||||
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
|
||||
|
||||
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
|
||||
avoid conflicts with `system.default`, so named argument usage for this
|
||||
parameter like `getOrDefault(..., default = ...)` will have to be changed.
|
||||
### Breaking changes in the standard library
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||
### Breaking changes in the compiler
|
||||
|
||||
|
||||
## Library additions
|
||||
|
||||
|
||||
## Library changes
|
||||
|
||||
- Added `os.delEnv` and `nimscript.delEnv`. (#11466)
|
||||
|
||||
- Enable Oid usage in hashtables. (#11472)
|
||||
|
||||
#
|
||||
# Language additions
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
## Compiler changes
|
||||
### Tool changes
|
||||
|
||||
|
||||
## Tool changes
|
||||
### Compiler changes
|
||||
|
||||
- VM can now cast integer type arbitrarily. (#11459)
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
55
changelog_v020.md
Normal file
55
changelog_v020.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# v0.20.2 - XXXX-XX-XX
|
||||
|
||||
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
|
||||
with the same data slice/index meaning. This is backwards compatible for
|
||||
calls *not* changing the `rfind` `start` parameter from its default. (#11487)
|
||||
|
||||
In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
|
||||
then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
|
||||
should minimize gotchas porting code from other languages like Python or C++.)
|
||||
|
||||
- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
|
||||
`-d:nimBinaryStdFiles` for a transition period.
|
||||
|
||||
### Breaking changes in the standard library
|
||||
|
||||
- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
|
||||
conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
|
||||
need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
|
||||
|
||||
|
||||
### Breaking changes in the compiler
|
||||
|
||||
|
||||
## Library additions
|
||||
|
||||
- `toOpenArray` is now available for the JS target.
|
||||
|
||||
## Library changes
|
||||
|
||||
- Fix async IO operations stalling even after socket is closed. (#11232)
|
||||
|
||||
- More informative error message for `streams.openFileStream`. (#11438)
|
||||
|
||||
|
||||
## Language additions
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- Better error message for IndexError for empty containers. (#11476)
|
||||
|
||||
- Fix regression in semfold for old right shift. (#11477)
|
||||
|
||||
- Fix for passing tuples as static params to macros. (#11423)
|
||||
|
||||
## Bugfixes
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are
|
||||
implemented. These are now implemented purely in ``system.nim`` without compiler
|
||||
support. There is a new "heterogeneous" slice type ``system.HSlice`` that takes 2
|
||||
support. There is a new "heterogenous" slice type ``system.HSlice`` that takes 2
|
||||
generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
|
||||
produced by ``system.^``.
|
||||
This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
|
||||
@@ -660,4 +660,4 @@ for i in a..b:
|
||||
- Fixed "C++: SIGABRT instead of IndexError for out-of-bounds"
|
||||
([#6512](https://github.com/nim-lang/Nim/issues/6512))
|
||||
- Fixed "An uncaught exception in cpp mode doesn't show the exception name/msg"
|
||||
([#6431](https://github.com/nim-lang/Nim/issues/6431))
|
||||
([#6431](https://github.com/nim-lang/Nim/issues/6431))
|
||||
@@ -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
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
# v0.20.2 - 2019-07-17
|
||||
|
||||
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
|
||||
with the same data slice/index meaning. This is backwards compatible for
|
||||
calls *not* changing the `rfind` `start` parameter from its default. (#11487)
|
||||
|
||||
In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
|
||||
then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
|
||||
should minimize gotchas porting code from other languages like Python or C++.)
|
||||
|
||||
- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
|
||||
`-d:nimBinaryStdFiles` for a transition period.
|
||||
|
||||
### Breaking changes in the standard library
|
||||
|
||||
- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
|
||||
conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
|
||||
need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
|
||||
|
||||
|
||||
### Breaking changes in the compiler
|
||||
|
||||
|
||||
## Library additions
|
||||
|
||||
- `toOpenArray` is now available for the JS target.
|
||||
|
||||
## Library changes
|
||||
|
||||
- Fix async IO operations stalling even after socket is closed. (#11232)
|
||||
|
||||
- More informative error message for `streams.openFileStream`. (#11438)
|
||||
|
||||
|
||||
## Language additions
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- Better error message for IndexError for empty containers. (#11476)
|
||||
|
||||
- Fix regression in semfold for old right shift. (#11477)
|
||||
|
||||
- Fix for passing tuples as static params to macros. (#11423)
|
||||
|
||||
## Bugfixes
|
||||
@@ -1,138 +0,0 @@
|
||||
# v1.0 - 2019-09-23
|
||||
|
||||
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- The switch ``-d:nimBinaryStdFiles`` does not exist anymore. Instead
|
||||
stdin/stdout/stderr are binary files again. This change only affects
|
||||
Windows.
|
||||
|
||||
- On Windows console applications the code-page is set at program startup
|
||||
to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this
|
||||
feature.
|
||||
|
||||
- The language definition and compiler are now stricter about ``gensym``'ed
|
||||
symbols in hygienic templates. See the section in the
|
||||
[manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates)
|
||||
for further details. Use the compiler switch `--oldgensym:on` for a
|
||||
transition period.
|
||||
|
||||
|
||||
### Breaking changes in the standard library
|
||||
|
||||
- We removed `unicode.Rune16` without any deprecation period as the name
|
||||
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
|
||||
and we didn't find any usages of it in the wild. If you still need it, add this
|
||||
piece of code to your project:
|
||||
```nim
|
||||
type
|
||||
Rune16* = distinct int16
|
||||
```
|
||||
|
||||
- `exportc` now uses C instead of C++ mangling with `nim cpp`, matching behavior
|
||||
of `importc`, see #10578.
|
||||
Use the new `exportcpp` to mangle as C++ when using `nim cpp`.
|
||||
|
||||
|
||||
### Breaking changes in the compiler
|
||||
|
||||
- A bug allowing `int` to be implicitly converted to range types of smaller size
|
||||
(e.g `range[0'i8..10'i8]`) has been fixed.
|
||||
|
||||
|
||||
|
||||
## Library additions
|
||||
|
||||
- `encodings.getCurrentEncoding` now distinguishes between the console's
|
||||
encoding and the OS's encoding. This distinction is only meaningful on
|
||||
Windows.
|
||||
- Added `system.getOsFileHandle` which is usually more useful
|
||||
than `system.getFileHandle`. This distinction is only meaningful on
|
||||
Windows.
|
||||
- Added a `json.parseJsonFragments` iterator that can be used to speedup
|
||||
JSON processing substantially when there are JSON fragments separated
|
||||
by whitespace.
|
||||
|
||||
|
||||
|
||||
## Library changes
|
||||
|
||||
- Added `os.delEnv` and `nimscript.delEnv`. (#11466)
|
||||
|
||||
- Enabled Oid usage in hashtables. (#11472)
|
||||
|
||||
- Added `unsafeColumnAt` procs, that return unsafe cstring from InstantRow. (#11647)
|
||||
|
||||
- Make public `Sha1Digest` and `Sha1State` types and `newSha1State`,
|
||||
`update` and `finalize` procedures from `sha1` module. (#11694)
|
||||
|
||||
- Added the `std/monotimes` module which implements monotonic timestamps.
|
||||
|
||||
- Consistent error handling of two `exec` overloads. (#10967)
|
||||
|
||||
- Officially the following modules now have an unstable API:
|
||||
- std/varints
|
||||
- core/allocators
|
||||
- core/hotcodereloading
|
||||
- asyncstreams
|
||||
- base64
|
||||
- browsers
|
||||
- collections/rtarrays
|
||||
- collections/sharedlist
|
||||
- collections/sharedtable
|
||||
- concurrency/atomics
|
||||
- concurrency/cpuload
|
||||
- concurrency/threadpool
|
||||
- coro
|
||||
- endians
|
||||
- httpcore
|
||||
- parsesql
|
||||
- pathnorm
|
||||
- reservedmem
|
||||
- typetraits
|
||||
|
||||
Every other stdlib module is API stable with respect to version 1.
|
||||
|
||||
|
||||
|
||||
## Language additions
|
||||
|
||||
- Inline iterators returning `lent T` types are now supported, similarly to
|
||||
iterators returning `var T`:
|
||||
```nim
|
||||
iterator myitems[T](x: openarray[T]): lent T
|
||||
iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T]
|
||||
```
|
||||
|
||||
- Added an `importjs` pragma that can now be used instead of `importcpp`
|
||||
and `importc` to import symbols from JavaScript. `importjs` for routines always
|
||||
takes a "pattern" for maximum flexibility.
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `uint64` is now finally a regular ordinal type. This means `high(uint64)` compiles
|
||||
and yields the correct value.
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
- The Nim compiler now does not recompile the Nim project via ``nim c -r`` if
|
||||
no dependent Nim file changed. This feature can be overridden by
|
||||
the ``--forceBuild`` command line option.
|
||||
- The Nim compiler now warns about unused module imports. You can use a
|
||||
top level ``{.used.}`` pragma in the module that you want to be importable
|
||||
without producing this warning.
|
||||
- The "testament" testing tool's name was changed
|
||||
from `tester` to `testament` and is generally available as a tool to run Nim
|
||||
tests automatically.
|
||||
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- VM can now cast integer type arbitrarily. (#11459)
|
||||
|
||||
|
||||
|
||||
## Bugfixes
|
||||
@@ -1,56 +0,0 @@
|
||||
# v1.0.2 - 2019-10-23
|
||||
|
||||
|
||||
## Bugfixes
|
||||
|
||||
* fixes the --verbosity:2 regression
|
||||
* Fixed "Fail to compile a file twice under Windows (v1.0 bug)." [#12242](https://github.com/nim-lang/Nim/issues/12242)
|
||||
* fix nimpretty removing space before pragma
|
||||
* JS: gensym is stricter for 'this'
|
||||
* Fixed "VM Assertion Error with newruntime" [#12294](https://github.com/nim-lang/Nim/issues/12294)
|
||||
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
|
||||
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
|
||||
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
|
||||
* Fix how `relativePath` handle case sensitiviy
|
||||
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
|
||||
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
|
||||
* VM: no special casing for big endian machines
|
||||
* Fixed "`internal error: environment misses` with a simple template inside one of Jester macros" [#12323](https://github.com/nim-lang/Nim/issues/12323)
|
||||
* nimsuggest: fix tcp socket leak
|
||||
* nimsuggest: fix tcp socket leak for epc backend
|
||||
* Fixed "`writeFile` and `write(f, str)` skip null bytes on Windows" [#12315](https://github.com/nim-lang/Nim/issues/12315)
|
||||
* Fixed "Crash in intsets symmetric_difference" [#12366](https://github.com/nim-lang/Nim/issues/12366)
|
||||
* Fixed "[regression] VM crash when dealing with var param of a proc result" [#12244](https://github.com/nim-lang/Nim/issues/12244)
|
||||
* fixes a koch regression that made 'koch boot --listcmd' not work anymore
|
||||
* Fixed "[regression] inconsistent signed int `mod` operator between runtime, compiletime, and semfold" [#12332](https://github.com/nim-lang/Nim/issues/12332)
|
||||
* Fixed "Boehm disables interior pointer checking" [#12286](https://github.com/nim-lang/Nim/issues/12286)
|
||||
* Fixes semCustomPragma when nkSym
|
||||
* Fixed yield in nkCheckedFieldExpr
|
||||
* Fixed "`randomize()` from `random` not working on JS" [#12418](https://github.com/nim-lang/Nim/issues/12418)
|
||||
* Fixed "Compiler crash with invalid object variant" [#12379](https://github.com/nim-lang/Nim/issues/12379)
|
||||
* fix type's case in random.nim
|
||||
* Fixed "Update docs with a better way to signal unimplemented methods" [#10804](https://github.com/nim-lang/Nim/issues/10804)
|
||||
* Fixed "Nim language manual, push pragma is not explained well" [#10824](https://github.com/nim-lang/Nim/issues/10824)
|
||||
* Fixed "[regression] Importing more than one module with same name from different packages produce bad codegen" [#12420](https://github.com/nim-lang/Nim/issues/12420)
|
||||
* Namespace unittest enums to avoid name conflicts
|
||||
* Fixed "VM checks unsigned integers for overflow." [#12310](https://github.com/nim-lang/Nim/issues/12310)
|
||||
* Fixed "line directive is not generated for first line of function definition" [#12426](https://github.com/nim-lang/Nim/issues/12426)
|
||||
|
||||
|
||||
|
||||
## Documentation improvements
|
||||
|
||||
* 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)
|
||||
* Fix typo in docs
|
||||
* Fix reference to parseSpec proc in readme
|
||||
* [doc/tut1] removed discard discussion in comments
|
||||
* Documentation improvements around the db interface
|
||||
* Easier build instructions for windows - just run `build_all.bat`.
|
||||
* fix a few dead links and a missing sentence in documentation
|
||||
* Macro docs additions
|
||||
* Updated the code example in the os module to use better grammar.
|
||||
* Mention "lambdas" and `=>` in the manual
|
||||
* Better documentation on Garbage Collector
|
||||
@@ -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 `sharedlist.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,330 +0,0 @@
|
||||
# v2.0.0 - 2023-08-01
|
||||
|
||||
Version 2.0 is a big milestone with too many changes to list them all here.
|
||||
|
||||
For a full list see [details](changelog_2_0_0_details.html).
|
||||
|
||||
|
||||
## New features
|
||||
|
||||
### Better tuple unpacking
|
||||
|
||||
Tuple unpacking for variables is now treated as syntax sugar that directly
|
||||
expands into multiple assignments. Along with this, tuple unpacking for
|
||||
variables can now be nested.
|
||||
|
||||
```nim
|
||||
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
|
||||
|
||||
# Now nesting is supported!
|
||||
let (x, (_, y), _, z) = returnsNestedTuple()
|
||||
|
||||
```
|
||||
|
||||
### Improved type inference
|
||||
|
||||
A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases.
|
||||
|
||||
For example, code like the following now compiles:
|
||||
|
||||
```nim
|
||||
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
|
||||
```
|
||||
|
||||
### Forbidden Tags
|
||||
|
||||
[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition
|
||||
of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types.
|
||||
|
||||
For example:
|
||||
|
||||
```nim
|
||||
|
||||
type IO = object ## input/output effect
|
||||
proc readLine(): string {.tags: [IO].} = discard
|
||||
proc echoLine(): void = discard
|
||||
|
||||
proc no_IO_please() {.forbids: [IO].} =
|
||||
# this is OK because it didn't define any tag:
|
||||
echoLine()
|
||||
# the compiler prevents this:
|
||||
let y = readLine()
|
||||
|
||||
```
|
||||
|
||||
### New standard library modules
|
||||
|
||||
The famous `os` module got an overhaul. Several of its features are available
|
||||
under a new interface that introduces a `Path` abstraction. A `Path` is
|
||||
a `distinct string`, which improves the type safety when dealing with paths, files
|
||||
and directories.
|
||||
|
||||
Use:
|
||||
|
||||
- `std/oserrors` for OS error reporting.
|
||||
- `std/envvars` for environment variables handling.
|
||||
- `std/paths` for path handling.
|
||||
- `std/dirs` for directory creation/deletion/traversal.
|
||||
- `std/files` for file existence checking, file deletions and moves.
|
||||
- `std/symlinks` for symlink handling.
|
||||
- `std/appdirs` for accessing configuration/home/temp directories.
|
||||
- `std/cmdline` for reading command line parameters.
|
||||
|
||||
### Consistent underscore handling
|
||||
|
||||
The underscore identifier (`_`) is now generally not added to scope when
|
||||
used as the name of a definition. While this was already the case for
|
||||
variables, it is now also the case for routine parameters, generic
|
||||
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
|
||||
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
|
||||
echo foo[int]()
|
||||
|
||||
proc _() = echo "_"
|
||||
_()
|
||||
|
||||
type _ = int
|
||||
let x: _ = 3
|
||||
```
|
||||
|
||||
Whereas the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
|
||||
proc foo[_, _](): int = 123
|
||||
echo foo[int, bool]()
|
||||
|
||||
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
|
||||
echo foo(int, bool)
|
||||
|
||||
proc _() = echo "one"
|
||||
proc _() = echo "two"
|
||||
|
||||
type _ = int
|
||||
type _ = float
|
||||
```
|
||||
|
||||
### JavaScript codegen improvement
|
||||
|
||||
The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
||||
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
|
||||
JS code generation, code using these types to interface with the JS backend
|
||||
may need to be updated. Note that `int` and `uint` are not affected.
|
||||
|
||||
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
|
||||
and in the case of potential bugs with the new implementation, the
|
||||
old behavior is currently still supported with the command line option
|
||||
`--jsbigint64:off`.
|
||||
|
||||
|
||||
## Docgen improvements
|
||||
|
||||
`Markdown` is now the default markup language of doc comments (instead
|
||||
of the legacy `RstMarkdown` mode). In this release we begin to separate
|
||||
RST and Markdown features to better follow specification of each
|
||||
language, with the focus on Markdown development.
|
||||
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
|
||||
|
||||
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
|
||||
select the markup language mode in the doc comments of the current `.nim`
|
||||
file for processing by `nim doc`:
|
||||
|
||||
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
|
||||
some Pandoc Markdown features + some RST features that are missing
|
||||
in our current implementation of CommonMark and Pandoc Markdown.
|
||||
2. `RST` closely follows the RST spec with few additional Nim features.
|
||||
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
|
||||
is kept for the sake of compatibility and ease of migration.
|
||||
|
||||
* Added separate `md2html` and `rst2html` commands for processing
|
||||
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
|
||||
|
||||
* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
|
||||
* Docgen now supports concise syntax for referencing Nim symbols:
|
||||
instead of specifying HTML anchors directly one can use original
|
||||
Nim symbol declarations (adding the aforementioned link brackets
|
||||
`[...]` around them).
|
||||
* To use this feature across modules, a new `importdoc` directive was added.
|
||||
Using this feature for referencing also helps to ensure that links
|
||||
(inside one module or the whole project) are not broken.
|
||||
* Added support for RST & Markdown quote blocks (blocks starting with `>`).
|
||||
* Added a popular Markdown definition lists extension.
|
||||
* Added Markdown indented code blocks (blocks indented by >= 4 spaces).
|
||||
* Added syntax for additional parameters to Markdown code blocks:
|
||||
|
||||
```nim test="nim c $1"
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## C++ interop enhancements
|
||||
|
||||
Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
|
||||
Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize
|
||||
the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types.
|
||||
|
||||
It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```cpp
|
||||
|
||||
struct Base {
|
||||
int someValue;
|
||||
Base(int inValue) {
|
||||
someValue = inValue;
|
||||
};
|
||||
};
|
||||
|
||||
class IPrinter {
|
||||
public:
|
||||
virtual void print() = 0;
|
||||
};
|
||||
```
|
||||
|
||||
```nim
|
||||
|
||||
type
|
||||
Base* {.importcpp, inheritable.} = object
|
||||
someValue*: int32
|
||||
IPrinter* {.importcpp.} = object
|
||||
|
||||
const objTemplate = """
|
||||
struct $1 : public $3, public IPrinter {
|
||||
$2
|
||||
};
|
||||
""";
|
||||
|
||||
type NimChild {.codegenDecl: objTemplate .} = object of Base
|
||||
|
||||
proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} =
|
||||
echo "It calls the base constructor passing " & $this.someValue
|
||||
this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`.
|
||||
|
||||
proc print*(self: NimChild) {.virtual.} =
|
||||
echo "Some value is " & $self.someValue
|
||||
|
||||
let child = makeNimChild(10)
|
||||
child.print()
|
||||
```
|
||||
|
||||
It outputs:
|
||||
|
||||
```
|
||||
It calls the base constructor passing 10
|
||||
Some value is 20
|
||||
```
|
||||
|
||||
|
||||
## ARC/ORC refinements
|
||||
|
||||
With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete:
|
||||
|
||||
1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable.
|
||||
2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations.
|
||||
3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter.
|
||||
|
||||
With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what?
|
||||
Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference.
|
||||
|
||||
But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of
|
||||
providing a stable ABI it is important not to lose any efficiency in the calling conventions.
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
|
||||
- nimgrep now offers the option `--inContext` (and `--notInContext`), which
|
||||
allows to filter only matches with the context block containing a given pattern.
|
||||
- nimgrep: names of options containing "include/exclude" are deprecated,
|
||||
e.g. instead of `--includeFile` and `--excludeFile` we have
|
||||
`--filename` and `--notFilename` respectively.
|
||||
Also, the semantics are now consistent for such positive/negative filters.
|
||||
- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions.
|
||||
|
||||
|
||||
## Porting guide
|
||||
|
||||
### Block and Break
|
||||
|
||||
Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn:
|
||||
|
||||
```nim
|
||||
|
||||
block:
|
||||
a()
|
||||
if cond:
|
||||
break
|
||||
b()
|
||||
|
||||
```
|
||||
|
||||
Into:
|
||||
|
||||
```nim
|
||||
|
||||
block maybePerformB:
|
||||
a()
|
||||
if cond:
|
||||
break maybePerformB
|
||||
b()
|
||||
|
||||
```
|
||||
|
||||
### Strict funcs
|
||||
|
||||
The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type Node = ref object
|
||||
s: string
|
||||
|
||||
func create(s: string): Node =
|
||||
result = Node()
|
||||
result.s = s # store to result[]
|
||||
|
||||
```
|
||||
|
||||
Now it has to be rewritten to:
|
||||
|
||||
```nim
|
||||
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type Node = ref object
|
||||
s: string
|
||||
|
||||
func create(s: string): Node =
|
||||
result = Node(s: s)
|
||||
|
||||
```
|
||||
|
||||
### Standard library
|
||||
|
||||
Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them:
|
||||
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
- `std/md5` => `checksums/md5`
|
||||
- `std/sha1` => `checksums/sha1`
|
||||
- `std/sums` => `sums`
|
||||
@@ -1,560 +0,0 @@
|
||||
# v2.0.0 - 2023-08-01
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- ORC is now the default memory management strategy. Use
|
||||
`--mm:refc` for a transition period.
|
||||
|
||||
- The `threads:on` option is now the default.
|
||||
|
||||
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`.
|
||||
|
||||
- `addr` is now available for all addressable locations,
|
||||
`unsafeAddr` is now deprecated and an alias for `addr`.
|
||||
|
||||
- Certain definitions from the default `system` module have been moved to
|
||||
the following new modules:
|
||||
|
||||
- `std/syncio`
|
||||
- `std/assertions`
|
||||
- `std/formatfloat`
|
||||
- `std/objectdollar`
|
||||
- `std/widestrs`
|
||||
- `std/typedthreads`
|
||||
- `std/sysatomics`
|
||||
|
||||
In the future, these definitions will be removed from the `system` module,
|
||||
and their respective modules will have to be imported to use them.
|
||||
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
|
||||
may be used.
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
|
||||
symbols in the `system` module:
|
||||
- Aliases with an `Error` suffix to exception types that have a `Defect` suffix
|
||||
(see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
|
||||
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
|
||||
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
|
||||
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
|
||||
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
|
||||
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
|
||||
`DeadThreadError`, `NilAccessError`
|
||||
- `addQuitProc`, replaced by `exitprocs.addExitProc`
|
||||
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
|
||||
- `TaintedString`, formerly a distinct alias to `string`
|
||||
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
|
||||
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
|
||||
in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`.
|
||||
|
||||
- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore.
|
||||
|
||||
- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to
|
||||
`pointer` anymore. `cast` may be used instead.
|
||||
|
||||
- The `gc:v2` option is removed.
|
||||
|
||||
- The `mainmodule` and `m` options are removed.
|
||||
|
||||
- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405))
|
||||
are now opt-in via `experimental:flexibleOptionalParams`.
|
||||
|
||||
- Automatic dereferencing (experimental feature) is removed.
|
||||
|
||||
- The `Math.trunc` polyfill for targeting Internet Explorer was
|
||||
previously included in most JavaScript output files.
|
||||
Now, it is only included with `-d:nimJsMathTruncPolyfill`.
|
||||
If you are targeting Internet Explorer, you may choose to enable this option
|
||||
or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
|
||||
Nim uses `Math.trunc` for the division and modulo operators for integers.
|
||||
|
||||
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
|
||||
`sink` for optimization purposes.
|
||||
|
||||
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
|
||||
|
||||
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
|
||||
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
|
||||
```nim
|
||||
type Foo = distinct ptr int
|
||||
|
||||
# Before:
|
||||
var x: Foo = nil
|
||||
# After:
|
||||
var x: Foo = Foo(nil)
|
||||
```
|
||||
- Removed two type pragma syntaxes deprecated since 0.20, namely
|
||||
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead,
|
||||
use `type Foo[T] {.final.} = object`.
|
||||
|
||||
- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
|
||||
with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
|
||||
This decision was made with the assumption that the old syntax was used rarely;
|
||||
if your code used the old syntax, please be aware of this change.
|
||||
|
||||
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
|
||||
are no longer experimental.
|
||||
|
||||
- `macros.getImpl` for `const` symbols now returns the full definition node
|
||||
(as `nnkConstDef`) rather than the AST of the constant value.
|
||||
|
||||
- Lock levels are deprecated, now a noop.
|
||||
|
||||
- `strictEffects` are no longer experimental.
|
||||
Use `legacy:laxEffects` to keep backward compatibility.
|
||||
|
||||
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
|
||||
if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`.
|
||||
|
||||
- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning.
|
||||
This warning will become an error in future versions! Use a `cast` operation
|
||||
like `cast[cstring](x)` instead.
|
||||
|
||||
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
|
||||
|
||||
- Redefining templates with the same signature was previously
|
||||
allowed to support certain macro code. To do this explicitly, the
|
||||
`{.redefine.}` pragma has been added. Note that this is only for templates.
|
||||
Implicit redefinition of templates is now deprecated and will give an error in the future.
|
||||
|
||||
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
|
||||
|
||||
- Several Standard libraries have been moved to nimble packages, use `nimble` to install them:
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
- `std/md5` => `checksums/md5`
|
||||
- `std/sha1` => `checksums/sha1`
|
||||
- `std/sums` => `std/sums`
|
||||
|
||||
- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
|
||||
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
||||
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
||||
|
||||
- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning:
|
||||
```nim
|
||||
try:
|
||||
discard
|
||||
except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept]
|
||||
discard
|
||||
```
|
||||
|
||||
- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
||||
|
||||
- The underscore identifier (`_`) is now generally not added to scope when
|
||||
used as the name of a definition. While this was already the case for
|
||||
variables, it is now also the case for routine parameters, generic
|
||||
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
|
||||
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
|
||||
echo foo[int]()
|
||||
|
||||
proc _() = echo "_"
|
||||
_()
|
||||
|
||||
type _ = int
|
||||
let x: _ = 3
|
||||
```
|
||||
|
||||
Whereas the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
|
||||
proc foo[_, _](): int = 123
|
||||
echo foo[int, bool]()
|
||||
|
||||
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
|
||||
echo foo(int, bool)
|
||||
|
||||
proc _() = echo "one"
|
||||
proc _() = echo "two"
|
||||
|
||||
type _ = int
|
||||
type _ = float
|
||||
```
|
||||
|
||||
- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
||||
|
||||
- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
||||
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
|
||||
JS code generation, code using these types to interface with the JS backend
|
||||
may need to be updated. Note that `int` and `uint` are not affected.
|
||||
|
||||
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
|
||||
and in the case of potential bugs with the new implementation, the
|
||||
old behavior is currently still supported with the command line option
|
||||
`--jsbigint64:off`.
|
||||
|
||||
- The `proc` and `iterator` type classes now respectively only match
|
||||
procs and iterators. Previously both type classes matched any of
|
||||
procs or iterators.
|
||||
|
||||
```nim
|
||||
proc prc(): int =
|
||||
123
|
||||
|
||||
iterator iter(): int {.closure.} =
|
||||
yield 123
|
||||
|
||||
proc takesProc[T: proc](x: T) = discard
|
||||
proc takesIter[T: iterator](x: T) = discard
|
||||
|
||||
# always compiled:
|
||||
takesProc(prc)
|
||||
takesIter(iter)
|
||||
# no longer compiles:
|
||||
takesProc(iter)
|
||||
takesIter(prc)
|
||||
```
|
||||
|
||||
- The `proc` and `iterator` type classes now accept a calling convention pragma
|
||||
(i.e. `proc {.closure.}`) that must be shared by matching proc or iterator
|
||||
types. Previously, pragmas were parsed but discarded if no parameter list
|
||||
was given.
|
||||
|
||||
This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with
|
||||
an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma
|
||||
node in the same place as in a concrete `proc` or `iterator` type node. This
|
||||
state of the AST may be unexpected to existing code, both due to the
|
||||
replacement of the `nnkFormalParams` node as well as having child nodes
|
||||
unlike other type class AST.
|
||||
|
||||
- Signed integer literals in `set` literals now default to a range type of
|
||||
`0..255` instead of `0..65535` (the maximum size of sets).
|
||||
|
||||
- `case` statements with `else` branches put before `elif`/`of` branches in macros
|
||||
are rejected with "invalid order of case branches".
|
||||
|
||||
- Destructors now default to `.raises: []` (i.e. destructors must not raise
|
||||
unlisted exceptions) and explicitly raising destructors are implementation
|
||||
defined behavior.
|
||||
|
||||
- The very old, undocumented `deprecated` pragma statement syntax for
|
||||
deprecated aliases is now a no-op. The regular deprecated pragma syntax is
|
||||
generally sufficient instead.
|
||||
|
||||
```nim
|
||||
# now does nothing:
|
||||
{.deprecated: [OldName: NewName].}
|
||||
|
||||
# instead use:
|
||||
type OldName* {.deprecated: "use NewName instead".} = NewName
|
||||
const oldName* {.deprecated: "use newName instead".} = newName
|
||||
```
|
||||
|
||||
`defined(nimalias)` can be used to check for versions when this syntax was
|
||||
available; however since code that used this syntax is usually very old,
|
||||
these deprecated aliases are likely not used anymore and it may make sense
|
||||
to simply remove these statements.
|
||||
|
||||
- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer
|
||||
declared when they are not available on the backend. Previously it would call
|
||||
`doAssert false` at runtime despite the condition being checkable at compile-time.
|
||||
|
||||
- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated.
|
||||
|
||||
- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
- OpenSSL 3 is now supported.
|
||||
- `macros.parseExpr` and `macros.parseStmt` now accept an optional
|
||||
`filename` argument for more informative errors.
|
||||
- The `colors` module is expanded with missing colors from the CSS color standard.
|
||||
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
|
||||
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
|
||||
- The `md5` module now works at compile time and in JavaScript.
|
||||
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables.
|
||||
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
|
||||
making limiting it to just the first char (`last = 0`) valid.
|
||||
- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator.
|
||||
- `random.rand` now works with `Ordinal`s.
|
||||
- Undeprecated `os.isvalidfilename`.
|
||||
- `std/oids` now uses `int64` to store time internally (before, it was int32).
|
||||
- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`.
|
||||
- `std/uri.Uri.isIpv6` is now exported.
|
||||
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
|
||||
|
||||
|
||||
- `std/jsfetch.newFetchOptions` now has default values for all parameters.
|
||||
- `std/jsformdata` now accepts the `Blob` data type.
|
||||
|
||||
- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433).
|
||||
|
||||
- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall.
|
||||
|
||||
This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
|
||||
|
||||
On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
|
||||
|
||||
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
|
||||
```sh
|
||||
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
|
||||
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
|
||||
$ sh build.sh # per unix install instructions
|
||||
$ bin/nim c koch # per unix install instructions
|
||||
$ ./koch boot -d:release # per unix install instructions
|
||||
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
|
||||
```
|
||||
|
||||
This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular).
|
||||
|
||||
[//]: # "Additions:"
|
||||
- Added ISO 8601 week date utilities in `times`:
|
||||
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
|
||||
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
|
||||
- Added an `initDateTime` overload to create a `DateTime` from an ISO week date.
|
||||
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
|
||||
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
|
||||
- Added new modules which were previously part of `std/os`:
|
||||
- Added `std/oserrors` for OS error reporting.
|
||||
- Added `std/envvars` for environment variables handling.
|
||||
- Added `std/cmdline` for reading command line parameters.
|
||||
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
|
||||
- Added `sep` parameter in `std/uri` to specify the query separator.
|
||||
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
|
||||
- Added `complex.sgn` for obtaining the phase of complex numbers.
|
||||
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
|
||||
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
|
||||
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
|
||||
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
|
||||
`toggleAttribute`, and `matches` to `std/dom`.
|
||||
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices).
|
||||
- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460).
|
||||
- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse.
|
||||
- Added a `safe` parameter to `base64.encodeMime`.
|
||||
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
|
||||
- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences.
|
||||
- `std/jscore` for the JavaScript target:
|
||||
+ Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
|
||||
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask).
|
||||
+ Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`.
|
||||
- Added `BackwardsIndex` overload for `CacheSeq`.
|
||||
- Added support for nested `with` blocks in `std/with`.
|
||||
- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time.
|
||||
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
- Deprecated `selfExe` for Nimscript.
|
||||
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
|
||||
Now only `byte` and `char` are supported.
|
||||
|
||||
[//]: # "Removals:"
|
||||
- Removed deprecated module `parseopt2`.
|
||||
- Removed deprecated module `sharedstrings`.
|
||||
- Removed deprecated module `dom_extensions`.
|
||||
- Removed deprecated module `LockFreeHash`.
|
||||
- Removed deprecated module `events`.
|
||||
- Removed deprecated `oids.oidToString`.
|
||||
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
|
||||
- Removed deprecated `jsre.test` and `jsre.toString`.
|
||||
- Removed deprecated `math.c_frexp`.
|
||||
- Removed deprecated `` httpcore.`==` ``.
|
||||
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types.
|
||||
- Removed deprecated `osproc.poDemon`, symbol with typo.
|
||||
- Removed deprecated `tables.rightSize`.
|
||||
|
||||
|
||||
- Removed deprecated `posix.CLONE_STOPPED`.
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma
|
||||
which can be used to disable certain effects in proc types.
|
||||
- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
|
||||
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
|
||||
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
|
||||
for the right-hand side of type definitions in type sections. Previously
|
||||
they would error with "invalid indentation".
|
||||
|
||||
- Compile-time define changes:
|
||||
- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
|
||||
In the command line, this is defined as `-d:a.b.c`. Older versions can
|
||||
use backticks as in ``defined(`a.b.c`)`` to access such defines.
|
||||
- [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
|
||||
now support a string argument for qualified define names.
|
||||
|
||||
```nim
|
||||
# -d:package.FooBar=42
|
||||
const FooBar {.intdefine: "package.FooBar".}: int = 5
|
||||
echo FooBar # 42
|
||||
```
|
||||
|
||||
This was added to help disambiguate similar define names for different packages.
|
||||
In older versions, this could only be achieved with something like the following:
|
||||
|
||||
```nim
|
||||
const FooBar = block:
|
||||
const `package.FooBar` {.intdefine.}: int = 5
|
||||
`package.FooBar`
|
||||
```
|
||||
- A generic `define` pragma for constants has been added that interprets
|
||||
the value of the define based on the type of the constant value.
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma)
|
||||
for a list of supported types.
|
||||
|
||||
- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
|
||||
- Templates now accept macro pragmas.
|
||||
- Macro pragmas for var/let/const sections have been redesigned in a way that works
|
||||
similarly to routine macro pragmas. The new behavior is documented in the
|
||||
[experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
|
||||
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
|
||||
allowing multiple type definitions to be injected in place of the original type definition.
|
||||
|
||||
```nim
|
||||
import macros
|
||||
|
||||
macro multiply(amount: static int, s: untyped): untyped =
|
||||
let name = $s[0].basename
|
||||
result = newNimNode(nnkTypeSection)
|
||||
for i in 1 .. amount:
|
||||
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
|
||||
|
||||
type
|
||||
Foo = object
|
||||
Bar {.multiply: 3.} = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
# becomes
|
||||
type
|
||||
Foo = object
|
||||
Bar1 = object
|
||||
x, y, z: int
|
||||
Bar2 = object
|
||||
x, y, z: int
|
||||
Bar3 = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
```
|
||||
|
||||
- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
|
||||
has been implemented for a variety of basic cases. For example, code like the following now compiles:
|
||||
|
||||
```nim
|
||||
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
|
||||
```
|
||||
|
||||
- `cstring` is now accepted as a selector in `case` statements, removing the
|
||||
need to convert to `string`. On the JS backend, this is translated directly
|
||||
to a `switch` statement.
|
||||
|
||||
- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
|
||||
- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
|
||||
|
||||
- IBM Z architecture and macOS m1 arm64 architecture are supported.
|
||||
|
||||
- `=wasMoved` can now be overridden by users.
|
||||
|
||||
- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code
|
||||
generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care.
|
||||
|
||||
- Tuple unpacking for variables is now treated as syntax sugar that directly
|
||||
expands into multiple assignments. Along with this, tuple unpacking for
|
||||
variables can now be nested.
|
||||
|
||||
```nim
|
||||
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
|
||||
|
||||
let (x, (_, y), _, z) = returnsNestedTuple()
|
||||
# roughly becomes
|
||||
let
|
||||
tmpTup1 = returnsNestedTuple()
|
||||
x = tmpTup1[0]
|
||||
tmpTup2 = tmpTup1[1]
|
||||
y = tmpTup2[1]
|
||||
z = tmpTup1[3]
|
||||
```
|
||||
|
||||
As a result `nnkVarTuple` nodes in variable sections will no longer be
|
||||
reflected in `typed` AST.
|
||||
|
||||
- C++ interoperability:
|
||||
- New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added.
|
||||
- Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
|
||||
reality better. (Nim moved away from all techniques based on "tracing".)
|
||||
|
||||
- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
|
||||
|
||||
- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
|
||||
without requiring `-d:nimVersion140` which is now a noop.
|
||||
|
||||
- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
|
||||
|
||||
- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
|
||||
related functions produced on the backend. This prevents conflicts with other Nim
|
||||
static libraries.
|
||||
|
||||
- When compiling for release, the flag `-fno-math-errno` is used for GCC.
|
||||
- Removed deprecated `LineTooLong` hint.
|
||||
- Line numbers and file names of source files work correctly inside templates for JavaScript targets.
|
||||
|
||||
- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers.
|
||||
|
||||
|
||||
## Docgen
|
||||
|
||||
- `Markdown` is now the default markup language of doc comments (instead
|
||||
of the legacy `RstMarkdown` mode). In this release we begin to separate
|
||||
RST and Markdown features to better follow specification of each
|
||||
language, with the focus on Markdown development.
|
||||
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
|
||||
|
||||
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
|
||||
select the markup language mode in the doc comments of the current `.nim`
|
||||
file for processing by `nim doc`:
|
||||
|
||||
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
|
||||
some Pandoc Markdown features + some RST features that are missing
|
||||
in our current implementation of CommonMark and Pandoc Markdown.
|
||||
2. `RST` closely follows the RST spec with few additional Nim features.
|
||||
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
|
||||
is kept for the sake of compatibility and ease of migration.
|
||||
|
||||
* Added separate `md2html` and `rst2html` commands for processing
|
||||
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
|
||||
|
||||
- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
|
||||
- Docgen now supports concise syntax for referencing Nim symbols:
|
||||
instead of specifying HTML anchors directly one can use original
|
||||
Nim symbol declarations (adding the aforementioned link brackets
|
||||
`[...]` around them).
|
||||
* To use this feature across modules, a new `importdoc` directive was added.
|
||||
Using this feature for referencing also helps to ensure that links
|
||||
(inside one module or the whole project) are not broken.
|
||||
- Added support for RST & Markdown quote blocks (blocks starting with `>`).
|
||||
- Added a popular Markdown definition lists extension.
|
||||
- Added Markdown indented code blocks (blocks indented by >= 4 spaces).
|
||||
- Added syntax for additional parameters to Markdown code blocks:
|
||||
|
||||
```nim test="nim c $1"
|
||||
...
|
||||
```
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
|
||||
- nimgrep added the option `--inContext` (and `--notInContext`), which
|
||||
allows to filter only matches with the context block containing a given pattern.
|
||||
- nimgrep: names of options containing "include/exclude" are deprecated,
|
||||
e.g. instead of `--includeFile` and `--excludeFile` we have
|
||||
`--filename` and `--notFilename` respectively.
|
||||
Also the semantics are now consistent for such positive/negative filters.
|
||||
- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice.
|
||||
@@ -1,248 +0,0 @@
|
||||
# v2.2.0 - 2024-10-02
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
|
||||
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
|
||||
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
|
||||
- `internalNew` is removed from the `system` module, use `new` instead.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- The JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
|
||||
|
||||
- The JS backend now supports closure iterators.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
|
||||
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to the `system` module, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
|
||||
- Inside a C++ constructor, `result` can be used to access the created object rather than `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
|
||||
@@ -1,23 +1,27 @@
|
||||
# v2.xx.x - yyyy-mm-dd
|
||||
## v0.XX.0 - XX/XX/2018
|
||||
|
||||
This is an example file.
|
||||
The changes should go to changelog.md!
|
||||
### Changes affecting backwards compatibility
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
- Example item: ``Foo`` changed to ``Bar``.
|
||||
|
||||
- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
- Added `example.exampleProc`.
|
||||
|
||||
- Changed `example.foo` to take additional `bar` parameter.
|
||||
|
||||
## Language changes
|
||||
#### Breaking changes in the standard library
|
||||
|
||||
|
||||
## Compiler changes
|
||||
#### Breaking changes in the compiler
|
||||
|
||||
### Library additions
|
||||
|
||||
### Library changes
|
||||
|
||||
|
||||
## Tool changes
|
||||
### Language additions
|
||||
|
||||
|
||||
### Language changes
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
### Compiler changes
|
||||
|
||||
### Bugfixes
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import std/[strutils, os, osproc, parseutils, strformat]
|
||||
|
||||
|
||||
proc main() =
|
||||
var msg = ""
|
||||
const cmd = "./koch boot --mm:orc -d:release"
|
||||
|
||||
let (output, exitCode) = execCmdEx(cmd)
|
||||
|
||||
doAssert exitCode == 0, output
|
||||
|
||||
let start = rfind(output, "Hint: mm")
|
||||
doAssert parseUntil(output, msg, "; proj", start) > 0, output
|
||||
|
||||
let (commitHash, _) = execCmdEx("""git log --format="%H" -n 1""")
|
||||
|
||||
let welcomeMessage = fmt"""Thanks for your hard work on this PR!
|
||||
The lines below are statistics of the Nim compiler built from {commitHash}
|
||||
|
||||
{msg}
|
||||
"""
|
||||
createDir "ci/nimcache"
|
||||
writeFile "ci/nimcache/results.txt", welcomeMessage
|
||||
|
||||
when isMainModule:
|
||||
main()
|
||||
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
|
||||
147
ci/funs.sh
147
ci/funs.sh
@@ -1,147 +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 ?
|
||||
if env | grep -q '^NIMCORES='; then
|
||||
echo $NIMCORES
|
||||
else
|
||||
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
|
||||
fi
|
||||
}
|
||||
|
||||
_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
|
||||
elif [ "$unamestr" = 'SunOS' ]; 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"
|
||||
@@ -1,128 +0,0 @@
|
||||
|
||||
import ast
|
||||
|
||||
import std / assertions
|
||||
|
||||
const
|
||||
PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
|
||||
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
||||
nkAddr, nkHiddenAddr,
|
||||
nkObjDownConv, nkObjUpConv}
|
||||
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
|
||||
|
||||
proc skipConvDfa*(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of nkObjDownConv, nkObjUpConv:
|
||||
result = result[0]
|
||||
of PathKinds1:
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||
var n = orig
|
||||
while true:
|
||||
case n.kind
|
||||
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
|
||||
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
|
||||
# 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
|
||||
(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
|
||||
#
|
||||
# let splited = split(move self.root, x)
|
||||
# self.root = merge(splited.lower, splited.greater)
|
||||
#
|
||||
# could be written without the ``move self.root``. However, this would be
|
||||
# wrong! Then the write barrier for the ``self.root`` assignment would
|
||||
# free the old data and all is lost! Lesson: Don't be too smart, trust the
|
||||
# lower level C++ optimizer to specialize this code.
|
||||
|
||||
type AliasKind* = enum
|
||||
yes, no, maybe
|
||||
|
||||
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[]: true
|
||||
# x[] -> x: 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, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
|
||||
result.add n
|
||||
n = n[0]
|
||||
of nkSym:
|
||||
result.add n
|
||||
break
|
||||
else: return no
|
||||
|
||||
collectImportantNodes(objImportantNodes, obj)
|
||||
collectImportantNodes(fieldImportantNodes, field)
|
||||
|
||||
# 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 nkDerefExpr, nkHiddenDeref:
|
||||
discard
|
||||
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
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
## Simple alias analysis for the HLO and the code generators.
|
||||
|
||||
import
|
||||
ast, astalgo, types, trees
|
||||
|
||||
import std/intsets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
ast, astalgo, types, trees, intsets, msgs
|
||||
|
||||
type
|
||||
TAnalysisResult* = enum
|
||||
@@ -27,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 ..< sonsLen(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 ..< sonsLen(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:
|
||||
@@ -51,16 +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.baseClass != nil:
|
||||
result = isPartOfAux(a.baseClass.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(skipModifier(a), b, marker)
|
||||
of tySet, tyArray:
|
||||
result = isPartOfAux(a.elementType, b, marker)
|
||||
of tyTuple:
|
||||
for aa in a.kids:
|
||||
result = isPartOfAux(aa, b, marker)
|
||||
result = isPartOfAux(lastSon(a), b, marker)
|
||||
of tyArray, tySet, tyTuple:
|
||||
for i in 0 ..< sonsLen(a):
|
||||
result = isPartOfAux(a.sons[i], b, marker)
|
||||
if result == arYes: return
|
||||
else: discard
|
||||
|
||||
@@ -81,29 +74,24 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
## cases:
|
||||
##
|
||||
## YES-cases:
|
||||
## ```
|
||||
## x <| x # for general trees
|
||||
## x[] <| x
|
||||
## x[i] <| x
|
||||
## x.f <| x
|
||||
## ```
|
||||
##
|
||||
## NO-cases:
|
||||
## ```
|
||||
## x !<| y # depending on type and symbol kind
|
||||
## x[constA] !<| x[constB]
|
||||
## x.f !<| x.g
|
||||
## x.f !<| y.f iff x !<= y
|
||||
## ```
|
||||
##
|
||||
## MAYBE-cases:
|
||||
##
|
||||
## ```
|
||||
## x[] ?<| y[] iff compatible type
|
||||
##
|
||||
##
|
||||
## x[] ?<| y depending on type
|
||||
## ```
|
||||
##
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym:
|
||||
@@ -118,11 +106,9 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
# use expensive type check:
|
||||
if isPartOf(a.sym.typ, b.sym.typ) != arNo:
|
||||
result = arMaybe
|
||||
else:
|
||||
result = arNo
|
||||
of nkBracketExpr:
|
||||
result = isPartOf(a[0], b[0])
|
||||
if a.len >= 2 and b.len >= 2:
|
||||
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;
|
||||
@@ -155,7 +141,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
result = isPartOf(a[1], b[1])
|
||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = isPartOf(a[0], b[0])
|
||||
else: result = arNo
|
||||
else: discard
|
||||
# Calls return a new location, so a default of ``arNo`` is fine.
|
||||
else:
|
||||
# go down recursively; this is quite demanding:
|
||||
@@ -171,7 +157,6 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
|
||||
of DerefKinds:
|
||||
# a* !<| b[] iff
|
||||
result = arNo
|
||||
if isPartOf(a.typ, b.typ) != arNo:
|
||||
result = isPartOf(a, b[0])
|
||||
if result == arNo: result = arMaybe
|
||||
@@ -193,26 +178,19 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
if isPartOf(a.typ, b.typ) != arNo:
|
||||
result = isPartOf(a[0], b)
|
||||
if result == arNo: result = arMaybe
|
||||
else:
|
||||
result = arNo
|
||||
else: result = arNo
|
||||
else: discard
|
||||
of nkObjConstr:
|
||||
result = arNo
|
||||
for i in 1..<b.len:
|
||||
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
|
||||
if res == arYes: break
|
||||
of nkBracket:
|
||||
if b.len > 0:
|
||||
result = isPartOf(a, b[0])
|
||||
else:
|
||||
result = arNo
|
||||
else: result = arNo
|
||||
else: discard
|
||||
|
||||
@@ -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,9 +69,9 @@ 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`.
|
||||
## delimted by `sep`, and alignment space is filled using `fill`.
|
||||
## More customized formatting can be done by calling `parseTableCells` directly.
|
||||
for cell in parseTableCells(s, delim):
|
||||
result.add cell.text
|
||||
1809
compiler/ast.nim
1809
compiler/ast.nim
File diff suppressed because it is too large
Load Diff
@@ -12,61 +12,72 @@
|
||||
# the data structures here are used in various places of the compiler.
|
||||
|
||||
import
|
||||
ast, astyaml, options, lineinfos, idents, rodutils,
|
||||
ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
|
||||
msgs
|
||||
|
||||
import std/[hashes, intsets]
|
||||
import std/strutils except addf
|
||||
|
||||
export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc hashNode*(p: RootRef): Hash
|
||||
proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
# Convert a tree into its YAML representation; this is used by the
|
||||
# YAML code generator and it is invaluable for debugging purposes.
|
||||
# If maxRecDepht <> -1 then it won't print the whole graph.
|
||||
proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
|
||||
|
||||
# these are for debugging only: They are not really deprecated, but I want
|
||||
# the warning so that release versions do not contain debugging statements:
|
||||
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.}
|
||||
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.}
|
||||
|
||||
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
|
||||
proc idTableGet*(t: TIdTable, key: int): RootRef
|
||||
proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef)
|
||||
proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
|
||||
# checks if `t` contains the `key` (compared by the pointer value, not only
|
||||
# `key`'s id)
|
||||
proc idNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
|
||||
proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
|
||||
proc lookupInRecord*(n: PNode, field: PIdent): PSym
|
||||
proc mustRehash*(length, counter: int): bool
|
||||
proc nextTry*(h, maxHash: Hash): Hash {.inline.}
|
||||
@@ -85,28 +96,28 @@ type
|
||||
data*: TIIPairSeq
|
||||
|
||||
|
||||
proc initIITable*(x: var TIITable)
|
||||
proc initIiTable*(x: var TIITable)
|
||||
proc iiTableGet*(t: TIITable, key: int): int
|
||||
proc iiTablePut*(t: var TIITable, key, val: int)
|
||||
|
||||
# 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 =
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) == getInt(b)
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
@@ -120,8 +131,8 @@ proc leValue*(a, b: PNode): bool =
|
||||
# a <= b?
|
||||
result = false
|
||||
case a.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) <= getInt(b)
|
||||
of nkCharLit..nkUInt32Lit:
|
||||
if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
@@ -141,17 +152,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 ..< sonsLen(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 ..< sonsLen(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:
|
||||
@@ -163,56 +174,14 @@ 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
|
||||
proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
|
||||
for i in start ..< sonsLen(list):
|
||||
if list.sons[i].kind == nkSym:
|
||||
result = list.sons[i].sym
|
||||
if result.name.id == ident.id: return
|
||||
else: return nil
|
||||
result = nil
|
||||
|
||||
proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
|
||||
result = false
|
||||
if a[0] != b[0]: return false
|
||||
var alen = a.len - 1
|
||||
while alen > 0 and a[alen] != '`': dec(alen)
|
||||
if alen <= 0: alen = a.len
|
||||
|
||||
var i = 1
|
||||
var j = 1
|
||||
while true:
|
||||
while i < alen and a[i] == '_': inc i
|
||||
while j < b.len and b[j] == '_': inc j
|
||||
var aa = if i < alen: toLowerAscii(a[i]) else: '\0'
|
||||
var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
|
||||
if aa != bb: return false
|
||||
|
||||
# the characters are identical:
|
||||
if i >= alen:
|
||||
# both cursors at the end:
|
||||
if j >= b.len: return true
|
||||
# not yet at the end of 'b':
|
||||
return false
|
||||
elif j >= b.len:
|
||||
return false
|
||||
inc i
|
||||
inc j
|
||||
|
||||
proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
|
||||
## Named parameters are special because a named parameter can be
|
||||
## gensym'ed and then they have '\`<number>' suffix that we need to
|
||||
## ignore, see compiler / evaltempl.nim, snippet:
|
||||
## ```nim
|
||||
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
|
||||
## if c.instLines: actual.info else: templ.info)
|
||||
## ```
|
||||
result = nil
|
||||
for i in 1..<list.len:
|
||||
let it = list[i].sym
|
||||
if it.name.id == ident.id or
|
||||
sameIgnoreBacktickGensymInfo(it.name.s, ident.s): return it
|
||||
|
||||
proc hashNode(p: RootRef): Hash =
|
||||
result = hash(cast[pointer](p))
|
||||
|
||||
@@ -220,7 +189,169 @@ proc mustRehash(length, counter: int): bool =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
import std/tables
|
||||
proc rspaces(x: int): Rope =
|
||||
# returns x spaces
|
||||
result = rope(spaces(x))
|
||||
|
||||
proc toYamlChar(c: char): string =
|
||||
case c
|
||||
of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
|
||||
of '\'', '\"', '\\': result = '\\' & c
|
||||
else: result = $c
|
||||
|
||||
proc makeYamlString*(s: string): Rope =
|
||||
# We have to split long strings into many ropes. Otherwise
|
||||
# this could trigger InternalError(111). See the ropes module for
|
||||
# further information.
|
||||
const MaxLineLength = 64
|
||||
result = nil
|
||||
var res = "\""
|
||||
for i in 0 ..< s.len:
|
||||
if (i + 1) mod MaxLineLength == 0:
|
||||
add(res, '\"')
|
||||
add(res, "\n")
|
||||
add(result, rope(res))
|
||||
res = "\"" # reset
|
||||
add(res, toYamlChar(s[i]))
|
||||
add(res, '\"')
|
||||
add(result, rope(res))
|
||||
|
||||
proc flagsToStr[T](flags: set[T]): Rope =
|
||||
if flags == {}:
|
||||
result = rope("[]")
|
||||
else:
|
||||
result = nil
|
||||
for x in items(flags):
|
||||
if result != nil: add(result, ", ")
|
||||
add(result, makeYamlString($x))
|
||||
result = "[" & result & "]"
|
||||
|
||||
proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
|
||||
result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
|
||||
rope(toLinenumber(info)),
|
||||
rope(toColumn(info))]
|
||||
|
||||
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
|
||||
indent, maxRecDepth: int): Rope
|
||||
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
|
||||
indent, maxRecDepth: int): Rope
|
||||
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
|
||||
indent, maxRecDepth: int): Rope
|
||||
|
||||
proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): Rope =
|
||||
if n == nil:
|
||||
result = rope("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
result = "\"$1\"" % [rope(n.name.s)]
|
||||
else:
|
||||
var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
|
||||
#rope("typ"), typeToYamlAux(conf, n.typ, marker,
|
||||
# indent + 2, maxRecDepth - 1),
|
||||
let istr = rspaces(indent + 2)
|
||||
result = rope("{")
|
||||
addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
|
||||
addf(result, "$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
|
||||
addf(result, "$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
|
||||
if conf != nil:
|
||||
# if we don't pass the config, we probably don't care about the line info
|
||||
addf(result, "$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
if card(n.flags) > 0:
|
||||
addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
|
||||
addf(result, "$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
|
||||
addf(result, "$N$1\"ast\": $2", [istr, ast])
|
||||
addf(result, "$N$1\"options\": $2", [istr, flagsToStr(n.options)])
|
||||
addf(result, "$N$1\"position\": $2", [istr, rope(n.position)])
|
||||
addf(result, "$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
|
||||
addf(result, "$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
|
||||
if card(n.loc.flags) > 0:
|
||||
addf(result, "$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
|
||||
addf(result, "$N$1\"r\": $2", [istr, n.loc.r])
|
||||
addf(result, "$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): Rope =
|
||||
var sonsRope: Rope
|
||||
if n == nil:
|
||||
sonsRope = rope("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
|
||||
else:
|
||||
if sonsLen(n) > 0:
|
||||
sonsRope = rope("[")
|
||||
for i in 0 ..< sonsLen(n):
|
||||
if i > 0: add(sonsRope, ",")
|
||||
addf(sonsRope, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i],
|
||||
marker, indent + 4, maxRecDepth - 1)])
|
||||
addf(sonsRope, "$N$1]", [rspaces(indent + 2)])
|
||||
else:
|
||||
sonsRope = rope("null")
|
||||
|
||||
let istr = rspaces(indent + 2)
|
||||
result = rope("{")
|
||||
addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
|
||||
addf(result, "$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
|
||||
addf(result, "$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
|
||||
if card(n.flags) > 0:
|
||||
addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
|
||||
addf(result, "$N$1\"callconv\": $2", [istr, makeYamlString(CallingConvToStr[n.callConv])])
|
||||
addf(result, "$N$1\"size\": $2", [istr, rope(n.size)])
|
||||
addf(result, "$N$1\"align\": $2", [istr, rope(n.align)])
|
||||
addf(result, "$N$1\"sons\": $2", [istr, sonsRope])
|
||||
|
||||
proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
|
||||
maxRecDepth: int): Rope =
|
||||
if n == nil:
|
||||
result = rope("null")
|
||||
else:
|
||||
var istr = rspaces(indent + 2)
|
||||
result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
|
||||
if maxRecDepth != 0:
|
||||
if conf != nil:
|
||||
addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
|
||||
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
addf(result, ",$N$1\"floatVal\": $2",
|
||||
[istr, rope(n.floatVal.toStrMaxPrecision)])
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||
of nkSym:
|
||||
addf(result, ",$N$1\"sym\": $2",
|
||||
[istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
|
||||
of nkIdent:
|
||||
if n.ident != nil:
|
||||
addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
|
||||
else:
|
||||
addf(result, ",$N$1\"ident\": null", [istr])
|
||||
else:
|
||||
if sonsLen(n) > 0:
|
||||
addf(result, ",$N$1\"sons\": [", [istr])
|
||||
for i in 0 ..< sonsLen(n):
|
||||
if i > 0: add(result, ",")
|
||||
addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i],
|
||||
marker, indent + 4, maxRecDepth - 1)])
|
||||
addf(result, "$N$1]", [istr])
|
||||
addf(result, ",$N$1\"typ\": $2",
|
||||
[istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
|
||||
addf(result, "$N$1}", [rspaces(indent)])
|
||||
|
||||
proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
|
||||
var marker = initIntSet()
|
||||
result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
import tables
|
||||
|
||||
const backrefStyle = "\e[90m"
|
||||
const enumStyle = "\e[34m"
|
||||
@@ -248,7 +379,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) =
|
||||
@@ -384,12 +515,14 @@ proc value(this: var DebugPrinter; value: PType) =
|
||||
this.key "n"
|
||||
this.value value.n
|
||||
|
||||
this.key "sons"
|
||||
this.openBracket
|
||||
for i, a in value.ikids:
|
||||
if i > 0: this.comma
|
||||
this.value a
|
||||
this.closeBracket
|
||||
if sonsLen(value) > 0:
|
||||
this.key "sons"
|
||||
this.openBracket
|
||||
for i in 0 ..< sonsLen(value):
|
||||
this.value value.sons[i]
|
||||
if i != sonsLen(value) - 1:
|
||||
this.comma
|
||||
this.closeBracket
|
||||
|
||||
if value.n != nil:
|
||||
this.key "n"
|
||||
@@ -403,26 +536,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"
|
||||
@@ -445,46 +568,43 @@ 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 sonsLen(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 ..< sonsLen(value):
|
||||
this.value value.sons[i]
|
||||
if i != sonsLen(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(
|
||||
visited: initTable[pointer, int](),
|
||||
renderSymType: true,
|
||||
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(
|
||||
visited: initTable[pointer, int](),
|
||||
renderSymType: true,
|
||||
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(
|
||||
visited: initTable[pointer, int](),
|
||||
renderSymType: false,
|
||||
useColor: not defined(windows)
|
||||
)
|
||||
this.value(n)
|
||||
echo($this.res)
|
||||
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
# For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
@@ -509,13 +629,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)
|
||||
|
||||
@@ -528,7 +648,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:
|
||||
@@ -571,21 +691,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
|
||||
@@ -601,11 +721,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:
|
||||
@@ -633,21 +752,16 @@ type
|
||||
name*: PIdent
|
||||
|
||||
proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||
# hot spots
|
||||
var h = ti.h and high(tab.data)
|
||||
var start = h
|
||||
var p {.cursor.} = tab.data[h]
|
||||
while p != nil:
|
||||
if p.name.id == ti.name.id: break
|
||||
result = tab.data[h]
|
||||
while result != nil:
|
||||
if result.name.id == ti.name.id: break
|
||||
h = nextTry(h, high(tab.data))
|
||||
if h == start:
|
||||
p = nil
|
||||
result = nil
|
||||
break
|
||||
p = tab.data[h]
|
||||
if p != nil:
|
||||
result = p # increase the count
|
||||
else:
|
||||
result = nil
|
||||
result = tab.data[h]
|
||||
ti.h = nextTry(h, high(tab.data))
|
||||
|
||||
proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
|
||||
@@ -707,16 +821,129 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
|
||||
result = nextIter(ti, tab)
|
||||
|
||||
iterator items*(tab: TStrTable): PSym =
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var it: TTabIter
|
||||
var s = initTabIter(it, tab)
|
||||
while s != nil:
|
||||
yield s
|
||||
s = nextIter(it, tab)
|
||||
|
||||
proc hasEmptySlot(data: TIdPairSeq): bool =
|
||||
for h in 0 .. high(data):
|
||||
if data[h].key == nil:
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc idTableRawGet(t: TIdTable, key: int): int =
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key.id == key:
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
|
||||
var index = idTableRawGet(t, key.id)
|
||||
if index >= 0: result = t.data[index].key == key
|
||||
else: result = false
|
||||
|
||||
proc idTableGet(t: TIdTable, key: PIdObj): RootRef =
|
||||
var index = idTableRawGet(t, key.id)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = nil
|
||||
|
||||
proc idTableGet(t: TIdTable, key: int): RootRef =
|
||||
var index = idTableRawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = nil
|
||||
|
||||
iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
|
||||
for i in 0..high(t.data):
|
||||
if t.data[i].key != nil:
|
||||
yield (t.data[i].key.id, t.data[i].val)
|
||||
|
||||
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
|
||||
var h: Hash
|
||||
h = key.id and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key.id != key.id)
|
||||
h = nextTry(h, high(data))
|
||||
assert(data[h].key == nil)
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
|
||||
var
|
||||
index: int
|
||||
n: TIdPairSeq
|
||||
index = idTableRawGet(t, key.id)
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
t.data[index].val = val
|
||||
else:
|
||||
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))
|
||||
swap(t.data, n)
|
||||
idTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
|
||||
for i in 0 .. high(t.data):
|
||||
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
|
||||
var h: Hash
|
||||
h = key.id and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key.id == key.id:
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
|
||||
var index: int
|
||||
index = idNodeTableRawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = nil
|
||||
|
||||
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
|
||||
var h: Hash
|
||||
h = key.id and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key.id != key.id)
|
||||
h = nextTry(h, high(data))
|
||||
assert(data[h].key == nil)
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
|
||||
var index = idNodeTableRawGet(t, key)
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
var n: TIdNodePairSeq
|
||||
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)
|
||||
idNodeTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
|
||||
for i in 0 .. high(t.data):
|
||||
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc initIITable(x: var TIITable) =
|
||||
x.counter = 0
|
||||
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
|
||||
@@ -747,25 +974,21 @@ 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)
|
||||
iiTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc listSymbolNames*(symbols: openArray[PSym]): string =
|
||||
result = ""
|
||||
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
|
||||
proc isAddrNode*(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkAddr, nkHiddenAddr: true
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
|
||||
else: false
|
||||
else: false
|
||||
|
||||
@@ -1,45 +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.genericHead.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 addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " [$1" % typ.kind.toHumanStr
|
||||
if typ.sym != nil:
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
|
||||
|
||||
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
|
||||
@@ -1,154 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# AST YAML printing
|
||||
|
||||
import "."/[ast, lineinfos, msgs, options, rodutils]
|
||||
import std/[intsets, strutils]
|
||||
|
||||
proc addYamlString*(res: var string; s: string) =
|
||||
res.add "\""
|
||||
for c in s:
|
||||
case c
|
||||
of '\0' .. '\x1F', '\x7F' .. '\xFF':
|
||||
res.add("\\u" & strutils.toHex(ord(c), 4))
|
||||
of '\"', '\\':
|
||||
res.add '\\' & c
|
||||
else:
|
||||
res.add c
|
||||
|
||||
res.add('\"')
|
||||
|
||||
proc makeYamlString(s: string): string =
|
||||
result = ""
|
||||
result.addYamlString(s)
|
||||
|
||||
proc flagsToStr[T](flags: set[T]): string =
|
||||
if flags == {}:
|
||||
result = "[]"
|
||||
else:
|
||||
result = ""
|
||||
for x in items(flags):
|
||||
if result != "":
|
||||
result.add(", ")
|
||||
result.addYamlString($x)
|
||||
result = "[" & result & "]"
|
||||
|
||||
proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
|
||||
result = "["
|
||||
result.addYamlString(toFilename(conf, info))
|
||||
result.addf ", $1, $2]", [toLinenumber(info), toColumn(info)]
|
||||
|
||||
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent, maxRecDepth: int)
|
||||
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent, maxRecDepth: int)
|
||||
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent, maxRecDepth: int)
|
||||
|
||||
proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent: int; maxRecDepth: int) =
|
||||
if n == nil:
|
||||
res.add("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
res.addYamlString(n.name.s)
|
||||
else:
|
||||
let istr = spaces(indent * 4)
|
||||
|
||||
res.addf("kind: $1", [makeYamlString($n.kind)])
|
||||
res.addf("\n$1name: $2", [istr, makeYamlString(n.name.s)])
|
||||
res.addf("\n$1typ: ", [istr])
|
||||
res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth - 1)
|
||||
if conf != nil:
|
||||
# if we don't pass the config, we probably don't care about the line info
|
||||
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
if card(n.flags) > 0:
|
||||
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
|
||||
res.addf("\n$1magic: $2", [istr, makeYamlString($n.magic)])
|
||||
res.addf("\n$1ast: ", [istr])
|
||||
res.treeToYamlAux(conf, n.ast, marker, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1options: $2", [istr, flagsToStr(n.options)])
|
||||
res.addf("\n$1position: $2", [istr, $n.position])
|
||||
res.addf("\n$1k: $2", [istr, makeYamlString($n.loc.k)])
|
||||
res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
|
||||
if card(n.loc.flags) > 0:
|
||||
res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
|
||||
res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
|
||||
res.addf("\n$1lode: $2", [istr])
|
||||
res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)
|
||||
|
||||
proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent: int; maxRecDepth: int) =
|
||||
if n == nil:
|
||||
res.add("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
res.addf "\"$1 @$2\"" % [$n.kind, strutils.toHex(cast[uint](n), sizeof(n) * 2)]
|
||||
else:
|
||||
let istr = spaces(indent * 4)
|
||||
res.addf("kind: $2", [istr, makeYamlString($n.kind)])
|
||||
res.addf("\n$1sym: ")
|
||||
res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth - 1)
|
||||
res.addf("\n$1n: ")
|
||||
res.treeToYamlAux(conf, n.n, marker, indent + 1, maxRecDepth - 1)
|
||||
if card(n.flags) > 0:
|
||||
res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
|
||||
res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)])
|
||||
res.addf("\n$1size: $2", [istr, $(n.size)])
|
||||
res.addf("\n$1align: $2", [istr, $(n.align)])
|
||||
if n.hasElementType:
|
||||
res.addf("\n$1sons:")
|
||||
for a in n.kids:
|
||||
res.addf("\n - ")
|
||||
res.typeToYamlAux(conf, a, marker, indent + 1, maxRecDepth - 1)
|
||||
|
||||
proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent: int;
|
||||
maxRecDepth: int) =
|
||||
if n == nil:
|
||||
res.add("null")
|
||||
else:
|
||||
var istr = spaces(indent * 4)
|
||||
res.addf("kind: $1" % [makeYamlString($n.kind)])
|
||||
|
||||
if maxRecDepth != 0:
|
||||
if conf != nil:
|
||||
res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
|
||||
case n.kind
|
||||
of nkCharLit .. nkInt64Lit:
|
||||
res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
|
||||
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision])
|
||||
of nkStrLit .. nkTripleStrLit:
|
||||
res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)])
|
||||
of nkSym:
|
||||
res.addf("\n$1sym: ", [istr])
|
||||
res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth)
|
||||
of nkIdent:
|
||||
if n.ident != nil:
|
||||
res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
|
||||
else:
|
||||
res.addf("\n$1ident: null", [istr])
|
||||
else:
|
||||
if n.len > 0:
|
||||
res.addf("\n$1sons: ", [istr])
|
||||
for i in 0 ..< n.len:
|
||||
res.addf("\n$1 - ", [istr])
|
||||
res.treeToYamlAux(conf, n[i], marker, indent + 1, maxRecDepth - 1)
|
||||
if n.typ != nil:
|
||||
res.addf("\n$1typ: ", [istr])
|
||||
res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth)
|
||||
|
||||
proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string =
|
||||
var marker = initIntSet()
|
||||
result = newStringOfCap(1024)
|
||||
result.treeToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
proc typeToYaml*(conf: ConfigRef; n: PType; indent: int = 0; maxRecDepth: int = -1): string =
|
||||
var marker = initIntSet()
|
||||
result = newStringOfCap(1024)
|
||||
result.typeToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
|
||||
proc symToYaml*(conf: ConfigRef; n: PSym; indent: int = 0; maxRecDepth: int = -1): string =
|
||||
var marker = initIntSet()
|
||||
result = newStringOfCap(1024)
|
||||
result.symToYamlAux(conf, n, marker, indent, maxRecDepth)
|
||||
@@ -10,9 +10,6 @@
|
||||
# this unit handles Nim sets; it implements bit sets
|
||||
# the code here should be reused in the Nim standard library
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
ElemType = byte
|
||||
TBitSet* = seq[ElemType] # we use byte here to avoid issues with
|
||||
@@ -26,41 +23,54 @@ const
|
||||
template modElemSize(arg: untyped): untyped = arg and 7
|
||||
template divElemSize(arg: untyped): untyped = arg shr 3
|
||||
|
||||
proc bitSetIn*(x: TBitSet, e: BiggestInt): bool =
|
||||
proc bitSetInit*(b: var TBitSet, length: int)
|
||||
proc bitSetUnion*(x: var TBitSet, y: TBitSet)
|
||||
proc bitSetDiff*(x: var TBitSet, y: TBitSet)
|
||||
proc bitSetSymDiff*(x: var TBitSet, y: TBitSet)
|
||||
proc bitSetIntersect*(x: var TBitSet, y: TBitSet)
|
||||
proc bitSetIncl*(x: var TBitSet, elem: BiggestInt)
|
||||
proc bitSetExcl*(x: var TBitSet, elem: BiggestInt)
|
||||
proc bitSetIn*(x: TBitSet, e: BiggestInt): bool
|
||||
proc bitSetEquals*(x, y: TBitSet): bool
|
||||
proc bitSetContains*(x, y: TBitSet): bool
|
||||
proc bitSetCard*(x: TBitSet): BiggestInt
|
||||
# implementation
|
||||
|
||||
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
||||
result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero
|
||||
|
||||
proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) =
|
||||
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||
assert(elem >= 0)
|
||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
|
||||
(One shl elem.modElemSize)
|
||||
|
||||
proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) =
|
||||
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
|
||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
|
||||
not(One shl elem.modElemSize)
|
||||
|
||||
proc bitSetInit*(b: var TBitSet, length: int) =
|
||||
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]
|
||||
proc bitSetUnion(x: var TBitSet, y: TBitSet) =
|
||||
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]
|
||||
proc bitSetDiff(x: var TBitSet, y: TBitSet) =
|
||||
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]
|
||||
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
|
||||
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]
|
||||
proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
|
||||
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):
|
||||
proc bitSetEquals(x, y: TBitSet): bool =
|
||||
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):
|
||||
proc bitSetContains(x, y: TBitSet): bool =
|
||||
for i in 0 .. high(x):
|
||||
if (x[i] and not y[i]) != Zero:
|
||||
return false
|
||||
result = true
|
||||
@@ -86,12 +96,6 @@ const populationCount: array[uint8, uint8] = block:
|
||||
|
||||
arr
|
||||
|
||||
proc bitSetCard*(x: TBitSet): BiggestInt =
|
||||
result = 0
|
||||
proc bitSetCard(x: TBitSet): BiggestInt =
|
||||
for it in x:
|
||||
result.inc int(populationCount[it])
|
||||
|
||||
proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt =
|
||||
result = 0
|
||||
for j in 0..<size:
|
||||
if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
|
||||
|
||||
@@ -10,16 +10,13 @@
|
||||
## BTree implementation with few features, but good enough for the
|
||||
## Nim compiler's needs.
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
const
|
||||
M = 512 # max children per B-tree node = M-1
|
||||
# (must be even and greater than 2)
|
||||
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
|
||||
@@ -38,41 +35,37 @@ template less(a, b): bool = cmp(a, b) < 0
|
||||
template eq(a, b): bool = cmp(a, b) == 0
|
||||
|
||||
proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
|
||||
result = default(Val)
|
||||
var x = b.root
|
||||
while x.isInternal:
|
||||
for j in 0..<x.entries:
|
||||
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:
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result.vals[j] = move h.vals[Mhalf + j]
|
||||
else:
|
||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||
for j in 0 ..< Mhalf:
|
||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||
|
||||
proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
|
||||
## split node in half
|
||||
@@ -86,16 +79,10 @@ 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):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
h.vals[i] = move h.vals[i-1]
|
||||
else:
|
||||
shallowCopy(h.vals[i], h.vals[i-1])
|
||||
shallowCopy(h.vals[i], h.vals[i-1])
|
||||
h.vals[j] = val
|
||||
else:
|
||||
var newLink: Node[Key, Val] = nil
|
||||
@@ -145,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:
|
||||
@@ -178,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 ..< sonsLen(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 .. sonsLen(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(sonsLen(t.n) == sonsLen(t))
|
||||
for i in 0 ..< sonsLen(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 ..< sonsLen(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 ..< sonsLen(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 ..< sonsLen(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)
|
||||
);""")
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
type
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
@@ -1,338 +0,0 @@
|
||||
type VarKind = enum
|
||||
Local
|
||||
Global
|
||||
Threadvar
|
||||
Const
|
||||
AlwaysConst ## const even on C++
|
||||
|
||||
proc addVarHeader(builder: var Builder, kind: VarKind) =
|
||||
## adds modifiers for given var kind:
|
||||
## Local has no modifier
|
||||
## Global has `static` modifier
|
||||
## Const has `static NIM_CONST` modifier
|
||||
## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
|
||||
## Threadvar is unimplemented
|
||||
case kind
|
||||
of Local: discard
|
||||
of Global:
|
||||
builder.add("static ")
|
||||
of Const:
|
||||
builder.add("static NIM_CONST ")
|
||||
of AlwaysConst:
|
||||
builder.add("static const ")
|
||||
of Threadvar:
|
||||
doAssert false, "unimplemented"
|
||||
|
||||
proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
|
||||
## adds a variable declaration to the builder
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.add(";\n")
|
||||
|
||||
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
|
||||
## adds a variable declaration to the builder, with the `body` building the type
|
||||
builder.addVarHeader(kind)
|
||||
body
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(";\n")
|
||||
|
||||
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typeBody, initializerBody: typed) =
|
||||
## adds a variable declaration to the builder, with `typeBody` building the type, and
|
||||
## `initializerBody` building the initializer. initializer must be provided
|
||||
builder.addVarHeader(kind)
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.add(";\n")
|
||||
|
||||
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
|
||||
## adds an array variable declaration to the builder
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(elementType)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addInt(len)
|
||||
builder.add("]")
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.add(";\n")
|
||||
|
||||
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
|
||||
## adds an array variable declaration to the builder with the initializer built according to `body`
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(elementType)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addInt(len)
|
||||
builder.add("] = ")
|
||||
body
|
||||
builder.add(";\n")
|
||||
|
||||
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
|
||||
## adds a typedef declaration to the builder with name `name` and type as
|
||||
## built in `typeBody`
|
||||
builder.add("typedef ")
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(";\n")
|
||||
|
||||
type
|
||||
StructInitializerKind = enum
|
||||
siOrderedStruct ## struct constructor, but without named fields on C
|
||||
siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
|
||||
siArray ## array constructor
|
||||
siWrapper ## wrapper for a single field, generates it verbatim
|
||||
|
||||
StructInitializer = object
|
||||
## context for building struct initializers, i.e. `{ field1, field2 }`
|
||||
kind: StructInitializerKind
|
||||
## if true, fields will not be named, instead values are placed in order
|
||||
needsComma: bool
|
||||
|
||||
proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
|
||||
## starts building a struct initializer, i.e. braced initializer list
|
||||
result = StructInitializer(kind: kind, needsComma: false)
|
||||
if kind != siWrapper:
|
||||
builder.add("{")
|
||||
|
||||
template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
|
||||
## adds a field to a struct initializer, with the value built in `valueBody`
|
||||
if constr.needsComma:
|
||||
assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
|
||||
builder.add(", ")
|
||||
else:
|
||||
constr.needsComma = true
|
||||
case constr.kind
|
||||
of siArray, siWrapper:
|
||||
# no name, can just add value
|
||||
valueBody
|
||||
of siOrderedStruct:
|
||||
# no name, can just add value on C
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
valueBody
|
||||
of siNamedStruct:
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
builder.add(".")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
|
||||
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
|
||||
## finishes building a struct initializer
|
||||
if constr.kind != siWrapper:
|
||||
builder.add("}")
|
||||
|
||||
template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
|
||||
## builds a struct initializer, i.e. `{ field1, field2 }`
|
||||
## a `var StructInitializer` must be declared and passed as a parameter so
|
||||
## that it can be used with `addField`
|
||||
constr = builder.initStructInitializer(kind)
|
||||
body
|
||||
builder.finishStructInitializer(constr)
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
|
||||
## adds a field inside a struct/union type
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
|
||||
## adds an array field inside a struct/union type
|
||||
obj.add('\t')
|
||||
obj.add(elementType)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add("[")
|
||||
obj.addInt(len)
|
||||
obj.add("]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
|
||||
## adds an field inside a struct/union type, based on an `skField` symbol
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
type
|
||||
BaseClassKind = enum
|
||||
## denotes how and whether or not the base class/RTTI should be stored
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
StructBuilderInfo = object
|
||||
## context for building `struct` types
|
||||
baseKind: BaseClassKind
|
||||
named: bool
|
||||
preFieldsLen: int
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
|
||||
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
|
||||
obj.add("struct")
|
||||
if result.named:
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if baseType.len != 0:
|
||||
if m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
if result.baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.len
|
||||
if result.baseKind == bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
|
||||
proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.len:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
obj.add("}")
|
||||
|
||||
template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
|
||||
## builds a struct type not based on a Nim type with fields according to `body`,
|
||||
## `name` can be empty to build as a type expression and not a statement
|
||||
let info = startSimpleStruct(obj, m, name, baseType)
|
||||
body
|
||||
finishSimpleStruct(obj, m, info)
|
||||
|
||||
proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
|
||||
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
|
||||
if tfPacked in t.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(t))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(t))
|
||||
else:
|
||||
obj.add(structOrUnion(t))
|
||||
if result.named:
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if t.kind == tyObject:
|
||||
if t.baseClass == nil:
|
||||
if lacksMTypeField(t):
|
||||
result.baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
result.baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
result.baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
elif baseType.len != 0:
|
||||
if m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
if result.baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.len
|
||||
case result.baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if t.itemId notin m.g.graph.memberProcsPerType and
|
||||
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
|
||||
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
|
||||
proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.len and
|
||||
t.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
obj.add("}")
|
||||
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
obj.add("#pragma pack(pop)\n")
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
|
||||
## builds a struct type directly based on `typ` with fields according to `body`,
|
||||
## `name` can be empty to build as a type expression and not a statement
|
||||
let info = startStruct(obj, m, typ, name, baseType)
|
||||
body
|
||||
finishStruct(obj, m, typ, info)
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building the fields according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
@@ -1,25 +0,0 @@
|
||||
# XXX make complex ones like bitOr use builder instead
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
const
|
||||
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
|
||||
"N_STDCALL", "N_CDECL", "N_SAFECALL",
|
||||
"N_SYSCALL", # this is probably not correct for all platforms,
|
||||
# but one can #define it to what one wants
|
||||
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
|
||||
"N_NOCONV" #ccMember is N_NOCONV
|
||||
]
|
||||
|
||||
proc procPtrType(conv: TCallingConvention, rettype: Snippet, name: string): Snippet =
|
||||
CallingConvToStr[conv] & "_PTR(" & rettype & ", " & name & ")"
|
||||
|
||||
proc cCast(typ, value: Snippet): Snippet =
|
||||
"((" & typ & ") " & value & ")"
|
||||
|
||||
proc cAddr(value: Snippet): Snippet =
|
||||
"&" & value
|
||||
|
||||
proc bitOr(a, b: Snippet): Snippet =
|
||||
"(" & a & " | " & b & ")"
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,6 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
## This include file contains the logic to produce constant string
|
||||
## and seq literals. The code here is responsible that
|
||||
## ``const x = ["a", "b"]`` works without hidden runtime creation code.
|
||||
@@ -21,7 +19,7 @@ template detectVersion(field, corename) =
|
||||
if core == nil or core.kind != skConst:
|
||||
m.g.field = 1
|
||||
else:
|
||||
m.g.field = toInt(ast.getInt(core.astdef))
|
||||
m.g.field = int ast.getInt(core.ast)
|
||||
result = m.g.field
|
||||
|
||||
proc detectStrVersion(m: BModule): int =
|
||||
@@ -32,125 +30,79 @@ proc detectSeqVersion(m: BModule): int =
|
||||
|
||||
# ----- Version 1: GC'ed strings and seqs --------------------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
cgsym(m, "TGenericSeq")
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "Sup", typ = "TGenericSeq")
|
||||
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "Sup"):
|
||||
var seqInit: StructInitializer
|
||||
res.addStructInitializer(seqInit, kind = siOrderedStruct):
|
||||
res.addField(seqInit, name = "len"):
|
||||
res.add(rope(s.len))
|
||||
res.addField(seqInit, name = "reserved"):
|
||||
res.add(cCast("NI", bitOr(cCast("NU", rope(s.len)), "NIM_STRLIT_FLAG")))
|
||||
res.addField(strInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(res)
|
||||
proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
|
||||
discard cgsym(m, "TGenericSeq")
|
||||
result = getTempName(m)
|
||||
addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
|
||||
[result, makeCString(s), rope(len(s))])
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteralV1(m: BModule; n: PNode): Rope =
|
||||
if s.isNil:
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
|
||||
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
else:
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var name: string = ""
|
||||
if id == m.labels:
|
||||
# string literal not found in the cache:
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, name)
|
||||
result = ropecg(m, "((#NimStringDesc*) &$1)",
|
||||
[genStringLiteralDataOnlyV1(m, n.strVal)])
|
||||
else:
|
||||
name = m.tmpBase & $id
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name)))
|
||||
result = ropecg(m, "((#NimStringDesc*) &$1$2)",
|
||||
[m.tmpBase, id])
|
||||
|
||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = result):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "cap", typ = "NI")
|
||||
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
res.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
res.addField(structInit, name = "cap"):
|
||||
res.add(bitOr(rope(s.len), "NIM_STRLIT_FLAG"))
|
||||
res.addField(structInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(res)
|
||||
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; result: var Rope) =
|
||||
proc genStringLiteralV2(m: BModule; n: PNode): Rope =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var litName: string
|
||||
if id == m.labels:
|
||||
cgsym(m, "NimStrPayload")
|
||||
cgsym(m, "NimStringV2")
|
||||
let pureLit = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit)
|
||||
result = getTempName(m)
|
||||
discard cgsym(m, "NimStrPayload")
|
||||
discard cgsym(m, "NimStringV2")
|
||||
# string literal not found in the cache:
|
||||
litName = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst)
|
||||
addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, rope(len(n.strVal)), pureLit])
|
||||
else:
|
||||
litName = m.tmpBase & $id
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp):
|
||||
res.add("NimStringV2")
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "len"):
|
||||
res.add(rope(n.strVal.len))
|
||||
res.addField(strInit, name = "p"):
|
||||
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
|
||||
m.s[cfsStrData].add(res)
|
||||
result = getTempName(m)
|
||||
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; result: var Rope) =
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode): Rope =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var pureLit: Rope
|
||||
if id == m.labels:
|
||||
pureLit = getTempName(m)
|
||||
cgsym(m, "NimStrPayload")
|
||||
cgsym(m, "NimStringV2")
|
||||
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)
|
||||
var strInit: StructInitializer
|
||||
result.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
result.addField(strInit, name = "len"):
|
||||
result.add(rope(n.strVal.len))
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
result = "{$1, (NimStrPayload*)&$2}" % [rope(len(n.strVal)), pureLit]
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
isConst: bool; result: var Rope) =
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
|
||||
of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
|
||||
of 2:
|
||||
let tmp = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
|
||||
result.add tmp
|
||||
result = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, result)
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
|
||||
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteral(m: BModule; n: PNode): Rope =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralV1(m, n, result)
|
||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
||||
of 0, 1: result = genStringLiteralV1(m, n)
|
||||
of 2: result = genStringLiteralV2(m, n)
|
||||
else:
|
||||
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
||||
|
||||
294
compiler/ccgmerge.nim
Normal file
294
compiler/ccgmerge.nim
Normal file
@@ -0,0 +1,294 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the merge operation of 2 different C files. This
|
||||
## is needed for incremental compilation.
|
||||
|
||||
import
|
||||
ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
|
||||
intsets, platform, llstream, tables, sighashes, modulegraphs, pathutils
|
||||
|
||||
# Careful! Section marks need to contain a tabulator so that they cannot
|
||||
# be part of C string literals.
|
||||
|
||||
const
|
||||
CFileSectionNames: array[TCFileSection, string] = [
|
||||
cfsMergeInfo: "",
|
||||
cfsHeaders: "NIM_merge_HEADERS",
|
||||
cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
|
||||
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
||||
cfsTypes: "NIM_merge_TYPES",
|
||||
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
||||
cfsFieldInfo: "NIM_merge_FIELD_INFO",
|
||||
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
||||
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
||||
cfsVars: "NIM_merge_VARS",
|
||||
cfsData: "NIM_merge_DATA",
|
||||
cfsProcs: "NIM_merge_PROCS",
|
||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||
cfsDebugInit: "NIM_merge_DEBUG_INIT",
|
||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
|
||||
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
|
||||
]
|
||||
CProcSectionNames: array[TCProcSection, string] = [
|
||||
cpsLocals: "NIM_merge_PROC_LOCALS",
|
||||
cpsInit: "NIM_merge_PROC_INIT",
|
||||
cpsStmts: "NIM_merge_PROC_BODY"
|
||||
]
|
||||
NimMergeEndMark = "/*\tNIM_merge_END:*/"
|
||||
|
||||
proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = nil
|
||||
add(result, "\n/*\t")
|
||||
add(result, CFileSectionNames[fs])
|
||||
add(result, ":*/\n")
|
||||
|
||||
proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope(NimMergeEndMark & "\n")
|
||||
|
||||
proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope("")
|
||||
add(result, "\n/*\t")
|
||||
add(result, CProcSectionNames[ps])
|
||||
add(result, ":*/\n")
|
||||
|
||||
proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope(NimMergeEndMark & "\n")
|
||||
|
||||
proc writeTypeCache(a: TypeCache, s: var string) =
|
||||
var i = 0
|
||||
for id, value in pairs(a):
|
||||
if i == 10:
|
||||
i = 0
|
||||
s.add('\L')
|
||||
else:
|
||||
s.add(' ')
|
||||
encodeStr($id, s)
|
||||
s.add(':')
|
||||
encodeStr($value, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc writeIntSet(a: IntSet, s: var string) =
|
||||
var i = 0
|
||||
for x in items(a):
|
||||
if i == 10:
|
||||
i = 0
|
||||
s.add('\L')
|
||||
else:
|
||||
s.add(' ')
|
||||
encodeVInt(x, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc genMergeInfo*(m: BModule): Rope =
|
||||
if not compilationCachePresent(m.config): return nil
|
||||
var s = "/*\tNIM_merge_INFO:\n"
|
||||
s.add("typeCache:{")
|
||||
writeTypeCache(m.typeCache, s)
|
||||
s.add("declared:{")
|
||||
writeIntSet(m.declaredThings, s)
|
||||
when false:
|
||||
s.add("typeInfo:{")
|
||||
writeIntSet(m.typeInfoMarker, s)
|
||||
s.add("labels:")
|
||||
encodeVInt(m.labels, s)
|
||||
s.add(" flags:")
|
||||
encodeVInt(cast[int](m.flags), s)
|
||||
s.add("\n*/")
|
||||
result = s.rope
|
||||
|
||||
template `^`(pos: int): untyped = L.buf[pos]
|
||||
|
||||
proc skipWhite(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
while true:
|
||||
case ^pos
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of ' ': inc pos
|
||||
else: break
|
||||
L.bufpos = pos
|
||||
|
||||
proc skipUntilCmd(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
while true:
|
||||
case ^pos
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of '\0': break
|
||||
of '/':
|
||||
if ^(pos+1) == '*' and ^(pos+2) == '\t':
|
||||
inc pos, 3
|
||||
break
|
||||
inc pos
|
||||
else: inc pos
|
||||
L.bufpos = pos
|
||||
|
||||
proc atEndMark(buf: cstring, pos: int): bool =
|
||||
var s = 0
|
||||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||
result = s == NimMergeEndMark.len
|
||||
|
||||
proc readVerbatimSection(L: var TBaseLexer): Rope =
|
||||
var pos = L.bufpos
|
||||
var r = newStringOfCap(30_000)
|
||||
while true:
|
||||
case L.buf[pos]
|
||||
of CR:
|
||||
pos = nimlexbase.handleCR(L, pos)
|
||||
r.add('\L')
|
||||
of LF:
|
||||
pos = nimlexbase.handleLF(L, pos)
|
||||
r.add('\L')
|
||||
of '\0':
|
||||
doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
|
||||
break
|
||||
else:
|
||||
if atEndMark(L.buf, pos):
|
||||
inc pos, NimMergeEndMark.len
|
||||
break
|
||||
r.add(L.buf[pos])
|
||||
inc pos
|
||||
L.bufpos = pos
|
||||
result = r.rope
|
||||
|
||||
proc readKey(L: var TBaseLexer, result: var string) =
|
||||
var pos = L.bufpos
|
||||
setLen(result, 0)
|
||||
while L.buf[pos] in IdentChars:
|
||||
result.add(L.buf[pos])
|
||||
inc pos
|
||||
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
|
||||
while ^L.bufpos != '}':
|
||||
skipWhite(L)
|
||||
var key = decodeStr(L.buf, L.bufpos)
|
||||
if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
|
||||
inc 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) =
|
||||
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
|
||||
inc L.bufpos
|
||||
while ^L.bufpos != '}':
|
||||
skipWhite(L)
|
||||
var key = decodeVInt(L.buf, L.bufpos)
|
||||
result.incl(key)
|
||||
inc L.bufpos
|
||||
|
||||
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
var k = newStringOfCap("typeCache".len)
|
||||
while true:
|
||||
skipWhite(L)
|
||||
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
break
|
||||
readKey(L, k)
|
||||
case k
|
||||
of "typeCache": readTypeCache(L, m.typeCache)
|
||||
of "declared": readIntSet(L, m.declaredThings)
|
||||
of "typeInfo":
|
||||
when false: readIntSet(L, m.typeInfoMarker)
|
||||
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
||||
of "flags":
|
||||
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
|
||||
var L {.inject.}: TBaseLexer
|
||||
openBaseLexer(L, s)
|
||||
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
||||
while true:
|
||||
skipUntilCmd(L)
|
||||
if ^L.bufpos == '\0': break
|
||||
body
|
||||
closeBaseLexer(L)
|
||||
|
||||
proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
|
||||
## reads the merge meta information into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
processMergeInfo(L, m)
|
||||
break
|
||||
|
||||
type
|
||||
TMergeSections = object
|
||||
f: TCFileSections
|
||||
p: TCProcSections
|
||||
|
||||
proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
|
||||
## reads the merge sections into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
discard
|
||||
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
# read back into section
|
||||
skipWhite(L)
|
||||
var verbatim = readVerbatimSection(L)
|
||||
skipWhite(L)
|
||||
var sectionA = CFileSectionNames.find(k)
|
||||
if sectionA > 0 and sectionA <= high(TCFileSection).int:
|
||||
m.f[TCFileSection(sectionA)] = verbatim
|
||||
else:
|
||||
var sectionB = CProcSectionNames.find(k)
|
||||
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
|
||||
m.p[TCProcSection(sectionB)] = verbatim
|
||||
else:
|
||||
doAssert(false, "ccgmerge: unknown section: " & k)
|
||||
else:
|
||||
doAssert(false, "ccgmerge: '*/' expected")
|
||||
|
||||
proc mergeRequired*(m: BModule): bool =
|
||||
for i in cfsHeaders..cfsProcs:
|
||||
if m.s[i] != nil:
|
||||
#echo "not empty: ", i, " ", m.s[i]
|
||||
return true
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
if m.initProc.s(i) != nil:
|
||||
#echo "not empty: ", i, " ", m.initProc.s[i]
|
||||
return true
|
||||
|
||||
proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
|
||||
## merges the C file with the old version on hard disc.
|
||||
var old: TMergeSections
|
||||
readMergeSections(cfilename, old)
|
||||
# do the merge; old section before new section:
|
||||
for i in low(TCFileSection)..high(TCFileSection):
|
||||
m.s[i] = old.f[i] & m.s[i]
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
m.initProc.s(i) = old.p[i] & m.initProc.s(i)
|
||||
@@ -1,283 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the merge operation of 2 different C files. This
|
||||
## is needed for incremental compilation.
|
||||
|
||||
import
|
||||
ast, ropes, options, strutils, nimlexbase, cgendata, rodutils,
|
||||
intsets, llstream, tables, modulegraphs, pathutils
|
||||
|
||||
# Careful! Section marks need to contain a tabulator so that they cannot
|
||||
# be part of C string literals.
|
||||
|
||||
const
|
||||
CFileSectionNames: array[TCFileSection, string] = [
|
||||
cfsHeaders: "NIM_merge_HEADERS",
|
||||
cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
|
||||
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
||||
cfsTypes: "NIM_merge_TYPES",
|
||||
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
||||
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
||||
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
||||
cfsData: "NIM_merge_DATA",
|
||||
cfsVars: "NIM_merge_VARS",
|
||||
cfsProcs: "NIM_merge_PROCS",
|
||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
|
||||
]
|
||||
CProcSectionNames: array[TCProcSection, string] = [
|
||||
cpsLocals: "NIM_merge_PROC_LOCALS",
|
||||
cpsInit: "NIM_merge_PROC_INIT",
|
||||
cpsStmts: "NIM_merge_PROC_BODY"
|
||||
]
|
||||
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")
|
||||
|
||||
proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope(NimMergeEndMark & "\n")
|
||||
|
||||
proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope("")
|
||||
result.add("\n/*\t")
|
||||
result.add(CProcSectionNames[ps])
|
||||
result.add(":*/\n")
|
||||
|
||||
proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
|
||||
if compilationCachePresent(conf):
|
||||
result = rope(NimMergeEndMark & "\n")
|
||||
|
||||
proc writeTypeCache(a: TypeCache, s: var string) =
|
||||
var i = 0
|
||||
for id, value in pairs(a):
|
||||
if i == 10:
|
||||
i = 0
|
||||
s.add('\L')
|
||||
else:
|
||||
s.add(' ')
|
||||
encodeStr($id, s)
|
||||
s.add(':')
|
||||
encodeStr($value, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc writeIntSet(a: IntSet, s: var string) =
|
||||
var i = 0
|
||||
for x in items(a):
|
||||
if i == 10:
|
||||
i = 0
|
||||
s.add('\L')
|
||||
else:
|
||||
s.add(' ')
|
||||
encodeVInt(x, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc genMergeInfo*(m: BModule): Rope =
|
||||
if not compilationCachePresent(m.config): return nil
|
||||
var s = "/*\tNIM_merge_INFO:\n"
|
||||
s.add("typeCache:{")
|
||||
writeTypeCache(m.typeCache, s)
|
||||
s.add("declared:{")
|
||||
writeIntSet(m.declaredThings, s)
|
||||
when false:
|
||||
s.add("typeInfo:{")
|
||||
writeIntSet(m.typeInfoMarker, s)
|
||||
s.add("labels:")
|
||||
encodeVInt(m.labels, s)
|
||||
s.add(" flags:")
|
||||
encodeVInt(cast[int](m.flags), s)
|
||||
s.add("\n*/")
|
||||
result = s.rope
|
||||
|
||||
template `^`(pos: int): untyped = L.buf[pos]
|
||||
|
||||
proc skipWhite(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
while true:
|
||||
case ^pos
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of ' ': inc pos
|
||||
else: break
|
||||
L.bufpos = pos
|
||||
|
||||
proc skipUntilCmd(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
while true:
|
||||
case ^pos
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of '\0': break
|
||||
of '/':
|
||||
if ^(pos+1) == '*' and ^(pos+2) == '\t':
|
||||
inc pos, 3
|
||||
break
|
||||
inc pos
|
||||
else: inc pos
|
||||
L.bufpos = pos
|
||||
|
||||
proc atEndMark(buf: cstring, pos: int): bool =
|
||||
var s = 0
|
||||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||
result = s == NimMergeEndMark.len
|
||||
|
||||
proc readVerbatimSection(L: var TBaseLexer): Rope =
|
||||
var pos = L.bufpos
|
||||
var r = newStringOfCap(30_000)
|
||||
while true:
|
||||
case L.buf[pos]
|
||||
of CR:
|
||||
pos = nimlexbase.handleCR(L, pos)
|
||||
r.add('\L')
|
||||
of LF:
|
||||
pos = nimlexbase.handleLF(L, pos)
|
||||
r.add('\L')
|
||||
of '\0':
|
||||
doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
|
||||
break
|
||||
else:
|
||||
if atEndMark(L.buf, pos):
|
||||
inc pos, NimMergeEndMark.len
|
||||
break
|
||||
r.add(L.buf[pos])
|
||||
inc pos
|
||||
L.bufpos = pos
|
||||
result = r.rope
|
||||
|
||||
proc readKey(L: var TBaseLexer, result: var string) =
|
||||
var pos = L.bufpos
|
||||
setLen(result, 0)
|
||||
while L.buf[pos] in IdentChars:
|
||||
result.add(L.buf[pos])
|
||||
inc pos
|
||||
if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
|
||||
L.bufpos = pos + 1 # skip ':'
|
||||
|
||||
proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
|
||||
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
|
||||
inc L.bufpos
|
||||
while ^L.bufpos != '}':
|
||||
skipWhite(L)
|
||||
var key = decodeStr(L.buf, L.bufpos)
|
||||
if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
|
||||
inc L.bufpos
|
||||
discard decodeStr(L.buf, L.bufpos)
|
||||
inc L.bufpos
|
||||
|
||||
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
|
||||
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
|
||||
inc L.bufpos
|
||||
while ^L.bufpos != '}':
|
||||
skipWhite(L)
|
||||
var key = decodeVInt(L.buf, L.bufpos)
|
||||
result.incl(key)
|
||||
inc L.bufpos
|
||||
|
||||
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
var k = newStringOfCap("typeCache".len)
|
||||
while true:
|
||||
skipWhite(L)
|
||||
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
break
|
||||
readKey(L, k)
|
||||
case k
|
||||
of "typeCache": readTypeCache(L, m.typeCache)
|
||||
of "declared": readIntSet(L, m.declaredThings)
|
||||
of "typeInfo":
|
||||
when false: readIntSet(L, m.typeInfoMarker)
|
||||
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
||||
of "flags":
|
||||
m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
|
||||
else: doAssert(false, "ccgmerge: unknown key: " & k)
|
||||
|
||||
template withCFile(cfilename: AbsoluteFile, body: untyped) =
|
||||
var s = llStreamOpen(cfilename, fmRead)
|
||||
if s == nil: return
|
||||
var L {.inject.}: TBaseLexer
|
||||
openBaseLexer(L, s)
|
||||
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
||||
while true:
|
||||
skipUntilCmd(L)
|
||||
if ^L.bufpos == '\0': break
|
||||
body
|
||||
closeBaseLexer(L)
|
||||
|
||||
proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
|
||||
## reads the merge meta information into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
processMergeInfo(L, m)
|
||||
break
|
||||
|
||||
type
|
||||
TMergeSections = object
|
||||
f: TCFileSections
|
||||
p: TCProcSections
|
||||
|
||||
proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
|
||||
## reads the merge sections into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
discard
|
||||
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
# read back into section
|
||||
skipWhite(L)
|
||||
var verbatim = readVerbatimSection(L)
|
||||
skipWhite(L)
|
||||
var sectionA = CFileSectionNames.find(k)
|
||||
if sectionA > 0 and sectionA <= high(TCFileSection).int:
|
||||
m.f[TCFileSection(sectionA)] = verbatim
|
||||
else:
|
||||
var sectionB = CProcSectionNames.find(k)
|
||||
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
|
||||
m.p[TCProcSection(sectionB)] = verbatim
|
||||
else:
|
||||
doAssert(false, "ccgmerge: unknown section: " & k)
|
||||
else:
|
||||
doAssert(false, "ccgmerge: '*/' expected")
|
||||
|
||||
proc mergeRequired*(m: BModule): bool =
|
||||
for i in cfsHeaders..cfsProcs:
|
||||
if m.s[i] != nil:
|
||||
#echo "not empty: ", i, " ", m.s[i]
|
||||
return true
|
||||
for i in TCProcSection:
|
||||
if m.initProc.s(i) != nil:
|
||||
#echo "not empty: ", i, " ", m.initProc.s[i]
|
||||
return true
|
||||
|
||||
proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
|
||||
## merges the C file with the old version on hard disc.
|
||||
var old: TMergeSections
|
||||
readMergeSections(cfilename, old)
|
||||
# do the merge; old section before new section:
|
||||
for i in TCFileSection:
|
||||
m.s[i] = old.f[i] & m.s[i]
|
||||
for i in TCProcSection:
|
||||
m.initProc.s(i) = old.p[i] & m.initProc.s(i)
|
||||
@@ -1,105 +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.snippet == "": 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.snippet])
|
||||
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.snippet], disc.loc.t)
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], 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, skipModifier(typ))
|
||||
of tyArray:
|
||||
let arraySize = lengthOrd(p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, arraySize])
|
||||
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
of tyObject:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
specializeResetT(p, accessor.parentObj(p.module), x)
|
||||
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
|
||||
|
||||
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, tyRange, 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:
|
||||
raiseAssert "unexpected set type kind"
|
||||
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
||||
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
||||
tyUncheckedArray, tyError, 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,34 +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.snippet])
|
||||
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.snippet])
|
||||
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 != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
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)
|
||||
var typedef = newBuilder("")
|
||||
typedef.addTypedef(name = "NimThreadVars"):
|
||||
typedef.addSimpleStruct(m, name = "", baseType = ""):
|
||||
typedef.add(m.g.nimtv)
|
||||
m.s[cfsSeqTypes].add(typedef)
|
||||
addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv != "":
|
||||
let externc = if m.config.backend == backendCpp or
|
||||
if m.g.nimtv != nil:
|
||||
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])
|
||||
|
||||
@@ -17,29 +17,29 @@ type
|
||||
visitorFrmt: string
|
||||
|
||||
const
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
|
||||
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
typ: PType) =
|
||||
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 ..< sonsLen(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
|
||||
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
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.snippet])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||
for i in 1 ..< sonsLen(n):
|
||||
let branch = n.sons[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
@@ -51,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if field.loc.r == nil: fillObjectFields(c.p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
|
||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||
else: internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
|
||||
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
||||
@@ -71,40 +71,40 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
case typ.kind
|
||||
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
||||
tySink, tyOwned:
|
||||
genTraverseProc(c, accessor, skipModifier(typ))
|
||||
genTraverseProc(c, accessor, lastSon(typ))
|
||||
of tyArray:
|
||||
let arraySize = lengthOrd(c.p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
let arraySize = lengthOrd(c.p.config, typ.sons[0])
|
||||
var i: TLoc
|
||||
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.snippet, arraySize])
|
||||
[i.r, arraySize])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
|
||||
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:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
genTraverseProc(c, accessor.parentObj(c.p.module), x)
|
||||
for i in 0 ..< sonsLen(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, a in typ.ikids:
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
|
||||
for i in 0 ..< sonsLen(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.elementType):
|
||||
elif containsGarbageCollectedRef(typ.lastSon):
|
||||
# destructor based seqs are themselves not traced but their data is, if
|
||||
# they contain a GC'ed type:
|
||||
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
|
||||
#genTraverseProcSeq(c, accessor, typ)
|
||||
genTraverseProcSeq(c, accessor, typ)
|
||||
of tyString:
|
||||
if tfHasAsgn notin typ.flags:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
@@ -117,15 +117,16 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
var p = c.p
|
||||
assert typ.kind == tySequence
|
||||
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
var a = TLoc(snippet: accessor)
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
|
||||
let oldCode = p.s(cpsStmts)
|
||||
var a: TLoc
|
||||
a.r = accessor
|
||||
|
||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, lenExpr(c.p, a)])
|
||||
[i.r, lenExpr(c.p, a)])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
|
||||
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
|
||||
@@ -133,6 +134,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
|
||||
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
var c: TTraversalClosure
|
||||
var p = newProc(nil, m)
|
||||
result = "Marker_" & getTypeName(m, origTyp, sig)
|
||||
let
|
||||
@@ -145,19 +147,18 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
lineF(p, cpsLocals, "$1 a;$n", [t])
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
|
||||
)
|
||||
c.p = p
|
||||
c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
|
||||
|
||||
assert typ.kind != tyTypeDesc
|
||||
if typ.kind == tySequence:
|
||||
genTraverseProcSeq(c, "a".rope, typ)
|
||||
else:
|
||||
if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
|
||||
if skipTypes(typ.sons[0], typedescInst+{tyOwned}).kind == tyArray:
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".rope, typ.elementType)
|
||||
genTraverseProc(c, "a".rope, typ.sons[0])
|
||||
else:
|
||||
genTraverseProc(c, "(*a)".rope, typ.elementType)
|
||||
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)]
|
||||
@@ -166,13 +167,14 @@ 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)
|
||||
var sLoc = rdLoc(s.loc)
|
||||
result = getTempName(m)
|
||||
@@ -181,10 +183,8 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
accessThreadLocalVar(p, s)
|
||||
sLoc = "NimTV_->" & sLoc
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
|
||||
)
|
||||
|
||||
c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
|
||||
c.p = p
|
||||
let header = "static N_NIMCALL(void, $1)(void)" % [result]
|
||||
genTraverseProc(c, sLoc, s.loc.t)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,27 +10,19 @@
|
||||
# This module declares some helpers for the C code generator.
|
||||
|
||||
import
|
||||
ast, types, msgs, wordrecg,
|
||||
platform, trees, options, cgendata, mangleutils
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg,
|
||||
platform, trees, options
|
||||
|
||||
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||
case n.kind
|
||||
of nkStmtList:
|
||||
result = nil
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
result = getPragmaStmt(n[i], w)
|
||||
if result != nil: break
|
||||
of nkPragma:
|
||||
result = nil
|
||||
for i in 0..<n.len:
|
||||
for i in 0 ..< n.len:
|
||||
if whichPragma(n[i]) == w: return n[i]
|
||||
else:
|
||||
result = nil
|
||||
else: discard
|
||||
|
||||
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
|
||||
result = getPragmaStmt(n, w) != nil
|
||||
@@ -41,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)
|
||||
@@ -51,14 +43,14 @@ 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)
|
||||
a = a + (a shl 3)
|
||||
a = a xor (a shr 11)
|
||||
a = a + (a shl 15)
|
||||
result = cast[Hash](uint(a))
|
||||
result = cast[Hash](a)
|
||||
|
||||
template getUniqueType*(key: PType): PType = key
|
||||
|
||||
@@ -68,103 +60,49 @@ proc makeSingleLineCString*(s: string): string =
|
||||
c.toCChar(result)
|
||||
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
|
||||
|
||||
#note precedence: params override types
|
||||
if optByRef in s.options: return true
|
||||
elif sfByCopy in s.flags: return false
|
||||
elif tfByRef in pt.flags: return true
|
||||
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 s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
|
||||
# bug #23354:
|
||||
result = false
|
||||
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
|
||||
proc mangle*(name: string): string =
|
||||
result = newStringOfCap(name.len)
|
||||
var start = 0
|
||||
if name[0] in Digits:
|
||||
result.add("X" & name[0])
|
||||
start = 1
|
||||
var requiresUnderscore = false
|
||||
template special(x) =
|
||||
result.add x
|
||||
requiresUnderscore = true
|
||||
for i in start..(name.len-1):
|
||||
let c = name[i]
|
||||
case c
|
||||
of 'a'..'z', '0'..'9', 'A'..'Z':
|
||||
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:
|
||||
add(result, c)
|
||||
of '$': special "dollar"
|
||||
of '%': special "percent"
|
||||
of '&': special "amp"
|
||||
of '^': special "roof"
|
||||
of '!': special "emark"
|
||||
of '?': special "qmark"
|
||||
of '*': special "star"
|
||||
of '+': special "plus"
|
||||
of '-': special "minus"
|
||||
of '/': special "slash"
|
||||
of '\\': special "backslash"
|
||||
of '=': special "eq"
|
||||
of '<': special "lt"
|
||||
of '>': special "gt"
|
||||
of '~': special "tilde"
|
||||
of ':': special "colon"
|
||||
of '.': special "dot"
|
||||
of '@': special "at"
|
||||
of '|': special "bar"
|
||||
else:
|
||||
result = 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)
|
||||
|
||||
proc encodeName*(name: string): string =
|
||||
result = mangle(name)
|
||||
result = $result.len & result
|
||||
|
||||
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result = if name == "": s.name.s else: name
|
||||
result.add "__"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
#Module::Type
|
||||
var name = s.name.s
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
case t.kind
|
||||
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
|
||||
result = encodeSym(m, t.sym)
|
||||
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
|
||||
result = encodeName(t[0].sym.name.s)
|
||||
result.add "I"
|
||||
for i in 1..<t.len - 1:
|
||||
result.add encodeType(m, t[i])
|
||||
result.add "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
else: encodeName(kindName)
|
||||
result.add "I"
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s)
|
||||
result.add "E"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
val.addFloat t.n[0].floatVal
|
||||
val.add "_"
|
||||
val.addFloat t.n[1].floatVal
|
||||
else:
|
||||
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
|
||||
result = encodeName(val)
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
add(result, "X" & toHex(ord(c), 2))
|
||||
requiresUnderscore = true
|
||||
if requiresUnderscore:
|
||||
result.add "_"
|
||||
|
||||
1803
compiler/cgen.nim
1803
compiler/cgen.nim
File diff suppressed because it is too large
Load Diff
@@ -10,14 +10,13 @@
|
||||
## This module contains the data structures for the C code generation phase.
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
ndi, lineinfos, pathutils, modulegraphs
|
||||
|
||||
import std/[intsets, tables, sets]
|
||||
ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
|
||||
tables, ndi, lineinfos, pathutils, modulegraphs
|
||||
|
||||
type
|
||||
TLabel* = Rope # for the C generator a label is just a rope
|
||||
TCFileSection* = enum # the sections a generated C file consists of
|
||||
cfsMergeInfo, # section containing merge information
|
||||
cfsHeaders, # section for C include file headers
|
||||
cfsFrameDefines # section for nim frame macros
|
||||
cfsForwardTypes, # section for C forward typedefs
|
||||
@@ -25,17 +24,21 @@ type
|
||||
cfsSeqTypes, # section for sequence types only
|
||||
# this is needed for strange type generation
|
||||
# reasons
|
||||
cfsTypeInfo, # section for type information (ag ABI checks)
|
||||
cfsFieldInfo, # section for field information
|
||||
cfsTypeInfo, # section for type information
|
||||
cfsProcHeaders, # section for C procs prototypes
|
||||
cfsStrData, # section for constant string literals
|
||||
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
|
||||
cfsTypeInit1, # section 1 for declarations of type information
|
||||
cfsTypeInit2, # section 2 for init of type information
|
||||
cfsTypeInit3, # section 3 for init of type information
|
||||
cfsDebugInit, # section for init of debug information
|
||||
cfsDynLibInit, # section for init of dynamic library binding
|
||||
cfsDynLibDeinit # section for deinitialization of dynamic
|
||||
# libraries
|
||||
TCTypeKind* = enum # describes the type kind of a C type
|
||||
ctVoid, ctChar, ctBool,
|
||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
||||
@@ -55,27 +58,22 @@ type
|
||||
id*: int # the ID of the label; positive means that it
|
||||
label*: Rope # generated text for the label
|
||||
# nil if label is not used
|
||||
sections*: TCProcSections # the code belonging
|
||||
sections*: TCProcSections # the code beloging
|
||||
isLoop*: bool # whether block is a loop
|
||||
nestedTryStmts*: int16 # how many try statements is it nested into
|
||||
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[n: 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
|
||||
@@ -88,20 +86,19 @@ type
|
||||
options*: TOptions # options that should be used for code
|
||||
# generation; this is the same as prc.options
|
||||
# unless prc == nil
|
||||
optionsStack*: seq[(TOptions, TNoteKinds)]
|
||||
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]
|
||||
inUncheckedAssignSection*: int
|
||||
|
||||
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
|
||||
@@ -111,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
|
||||
@@ -121,8 +117,9 @@ type
|
||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
||||
generatedHeader*: BModule
|
||||
breakPointId*: int
|
||||
breakpoints*: Rope # later the breakpoints are inserted into the main proc
|
||||
typeInfoMarker*: TypeCacheWithOwner
|
||||
typeInfoMarkerV2*: TypeCacheWithOwner
|
||||
config*: ConfigRef
|
||||
graph*: ModuleGraph
|
||||
strVersion*, seqVersion*: int # version of the string/seq implementation to use
|
||||
@@ -137,7 +134,6 @@ type
|
||||
# unconditionally...
|
||||
# nimtvDeps is VERY hard to cache because it's
|
||||
# not a list of IDs nor can it be made to be one.
|
||||
mangledPrcs*: HashSet[string]
|
||||
|
||||
TCGen = object of PPassContext # represents a C source file
|
||||
s*: TCFileSections # sections of the C file
|
||||
@@ -148,21 +144,15 @@ 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 occurrence 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
|
||||
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
|
||||
inHcrInitGuard*: bool # We are currently withing a HCR reloading guard.
|
||||
typeStack*: TTypeSeq # used for type generation
|
||||
dataCache*: TNodeTable
|
||||
typeNodes*, nimTypes*: int # used for type info generation
|
||||
@@ -170,13 +160,13 @@ 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
|
||||
|
||||
template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
|
||||
|
||||
proc includeHeader*(this: BModule; header: string) =
|
||||
if not this.headerFiles.contains header:
|
||||
@@ -184,32 +174,25 @@ 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
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
proc initBlock*(): TBlock =
|
||||
result = TBlock()
|
||||
for i in low(result.sections)..high(result.sections):
|
||||
result.sections[i] = newRopeAppender()
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result = BProc(
|
||||
prc: prc,
|
||||
module: module,
|
||||
optionsStack: if module.initProc != nil: module.initProc.optionsStack
|
||||
else: @[],
|
||||
options: if prc != nil: prc.options
|
||||
else: module.config.options,
|
||||
blocks: @[initBlock()],
|
||||
sigConflicts: initCountTable[string]())
|
||||
if optQuirky in result.options:
|
||||
result.flags = {nimErrorFlagDisabled}
|
||||
new(result)
|
||||
result.prc = prc
|
||||
result.module = module
|
||||
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,18 +7,11 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements code generation for methods.
|
||||
## This module implements code generation for multi methods.
|
||||
|
||||
import
|
||||
options, ast, msgs, idents, renderer, types, magicsys,
|
||||
sempass2, modulegraphs, lineinfos, astalgo
|
||||
|
||||
import std/intsets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import std/[tables]
|
||||
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
|
||||
sempass2, strutils, modulegraphs, lineinfos
|
||||
|
||||
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
||||
var dest = skipTypes(d, abstractPtrs)
|
||||
@@ -30,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:
|
||||
@@ -47,45 +40,39 @@ proc getDispatcher*(s: PSym): PSym =
|
||||
if dispatcherPos < s.ast.len:
|
||||
result = s.ast[dispatcherPos].sym
|
||||
doAssert sfDispatcher in result.flags
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||
result = n
|
||||
# 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 ..< sonsLen(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 =
|
||||
result = No
|
||||
if a.name.id != b.name.id: return
|
||||
if a.typ.signatureLen != b.typ.signatureLen:
|
||||
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||
return
|
||||
|
||||
var i = 0
|
||||
for x, y in paramTypePairs(a.typ, b.typ):
|
||||
inc i
|
||||
var aa = x
|
||||
var bb = y
|
||||
for i in 1 ..< sonsLen(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}:
|
||||
aa = aa.elementType
|
||||
bb = bb.elementType
|
||||
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}:
|
||||
aa = aa.lastSon
|
||||
bb = bb.lastSon
|
||||
else:
|
||||
break
|
||||
if sameType(x, y):
|
||||
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):
|
||||
@@ -103,45 +90,41 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
return No
|
||||
if result == Yes:
|
||||
# check for return type:
|
||||
# ignore flags of return types; # bug #22673
|
||||
if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
|
||||
if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
|
||||
if not sameTypeOrNil(a.typ.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.setReturnType a.typ.returnType
|
||||
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, 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, 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.loc.snippet = ""
|
||||
if s.typ.returnType != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
|
||||
disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||
disp.loc.r = nil
|
||||
if s.typ.sons[0] != nil:
|
||||
if disp.ast.sonsLen > resultPos:
|
||||
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))
|
||||
@@ -153,25 +136,35 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||
# from later definitions, particularly the resultPos slot. Also,
|
||||
# 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])
|
||||
if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
|
||||
disp.ast.sons[resultPos].kind == nkEmpty:
|
||||
disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
|
||||
|
||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
var witness: PSym = nil
|
||||
if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
|
||||
g.config.isDefined("nimInternalNonVtablesTesting"):
|
||||
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
|
||||
"` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
|
||||
if sfImportc in s.flags:
|
||||
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
|
||||
"` is not allowed to have 'importc' pragmas")
|
||||
# The following code works only with lock levels, so we disable
|
||||
# it when they're not available.
|
||||
when declared(TLockLevel):
|
||||
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
||||
proc `==`(a, b: TLockLevel): bool {.borrow.}
|
||||
if disp.typ.lockLevel == UnspecifiedLockLevel:
|
||||
disp.typ.lockLevel = meth.typ.lockLevel
|
||||
elif meth.typ.lockLevel != UnspecifiedLockLevel and
|
||||
meth.typ.lockLevel != disp.typ.lockLevel:
|
||||
message(conf, meth.info, warnLockLevel,
|
||||
"method has lock level $1, but another method has $2" %
|
||||
[$meth.typ.lockLevel, $disp.typ.lockLevel])
|
||||
# XXX The following code silences a duplicate warning in
|
||||
# checkMethodeffects() in sempass2.nim for now.
|
||||
if disp.typ.lockLevel < meth.typ.lockLevel:
|
||||
disp.typ.lockLevel = meth.typ.lockLevel
|
||||
|
||||
for i in 0..<g.methods.len:
|
||||
proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
|
||||
let L = len(g.methods)
|
||||
var witness: PSym
|
||||
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
|
||||
@@ -185,49 +178,44 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
of Invalid:
|
||||
if witness.isNil: witness = g.methods[i].methods[0]
|
||||
# create a new dispatcher:
|
||||
# stores the id and the position
|
||||
if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
|
||||
g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
|
||||
else:
|
||||
g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
|
||||
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
|
||||
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)
|
||||
elif sfBase notin s.flags:
|
||||
message(g.config, s.info, warnUseBase)
|
||||
|
||||
proc relevantCol*(methods: seq[PSym], col: int): bool =
|
||||
proc relevantCol(methods: seq[PSym], col: int): bool =
|
||||
# returns true iff the position is relevant
|
||||
result = false
|
||||
var t = methods[0].typ[col].skipTypes(skipPtrs)
|
||||
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 =
|
||||
result = 0
|
||||
for col in FirstParamAt..<a.typ.signatureLen:
|
||||
for col in 1 ..< sonsLen(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) =
|
||||
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:
|
||||
@@ -237,72 +225,74 @@ proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
|
||||
a[j] = v
|
||||
if h == 1: break
|
||||
|
||||
proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
|
||||
var base = methods[0].ast[dispatcherPos].sym
|
||||
result = base
|
||||
var paramLen = base.typ.signatureLen
|
||||
var paramLen = sonsLen(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 FirstParamAt..<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 FirstParamAt..<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.returnType
|
||||
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 generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
|
||||
for bucket in 0..<g.methods.len:
|
||||
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
for bucket in 0 ..< len(g.methods):
|
||||
var relevantCols = initIntSet()
|
||||
for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
|
||||
for col in 1 ..< sonsLen(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)
|
||||
g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)
|
||||
addSon(result,
|
||||
newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,33 +7,18 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest
|
||||
## Helpers for binaries that use compiler passes, eg: nim, nimsuggest, nimfix
|
||||
|
||||
import
|
||||
options, idents, nimconf, extccomp, commands, msgs,
|
||||
lineinfos, modulegraphs, condsyms, pathutils
|
||||
|
||||
import std/[os, parseopt]
|
||||
|
||||
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
|
||||
options, idents, nimconf, scriptconfig, extccomp, commands, msgs,
|
||||
lineinfos, modulegraphs, condsyms, os, pathutils
|
||||
|
||||
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)
|
||||
@@ -41,45 +26,68 @@ 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 == "-":
|
||||
handleStdinInput(conf)
|
||||
if self.supportsStdinFile and conf.projectName == "-":
|
||||
conf.projectName = "stdinfile"
|
||||
conf.projectFull = AbsoluteFile "stdinfile"
|
||||
conf.projectPath = AbsoluteDir getCurrentDir()
|
||||
conf.projectIsStdin = true
|
||||
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
|
||||
# restores `conf.notes` after loading config files
|
||||
# because it has overwrites the notes when compiling the system module which
|
||||
# is a foreign module compared to the project
|
||||
if conf.cmd in cmdBackends:
|
||||
conf.notes = conf.mainPackageNotes
|
||||
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:
|
||||
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)
|
||||
# command line can overwite the config file's settings
|
||||
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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +0,0 @@
|
||||
include "../lib/system/compilation.nim"
|
||||
version = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
skipDirs = @["."]
|
||||
installDirs = @["compiler"]
|
||||
|
||||
import os
|
||||
|
||||
var compilerDir = ""
|
||||
|
||||
before install:
|
||||
rmDir("compiler")
|
||||
|
||||
let
|
||||
files = listFiles(".")
|
||||
dirs = listDirs(".")
|
||||
|
||||
mkDir("compiler")
|
||||
|
||||
for f in files:
|
||||
cpFile(f, "compiler" / f)
|
||||
|
||||
for d in dirs:
|
||||
cpDir(d, "compiler" / d)
|
||||
|
||||
requires "nim"
|
||||
@@ -1,343 +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, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
|
||||
import std/intsets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
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"), c.idgen, ow, info)
|
||||
s.typ = newType(tyTypeDesc, c.idgen, ow)
|
||||
s.typ.flags.incl {tfUnresolved, tfPacked}
|
||||
s.typ.add newType(tyEmpty, c.idgen, ow)
|
||||
addDecl(c, s, info)
|
||||
|
||||
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])
|
||||
of nkCommentStmt:
|
||||
result = n
|
||||
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.skipModifier, 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.hasElementType == a.hasElementType:
|
||||
if f.hasElementType:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
result = true # both lack it
|
||||
else:
|
||||
result = false
|
||||
|
||||
of tyGenericInvocation:
|
||||
result = false
|
||||
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
|
||||
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
|
||||
for i in FirstGenericParamAt ..< f.kidsLen:
|
||||
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.hasElementType and f.elementType.kind != tyNone:
|
||||
# also check the generic's constraints:
|
||||
let oldLen = m.inferred.len
|
||||
result = matchType(c, f.elementType, 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, last 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
|
||||
else:
|
||||
result = false
|
||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
|
||||
of tyVar, tySink, tyLent, tyOwned:
|
||||
# modifiers in the concept must be there in the actual implementation
|
||||
# too but not vice versa.
|
||||
if a.kind == f.kind:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
elif m.magic == mArrPut:
|
||||
result = matchType(c, f.elementType, 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.hasElementType and ak.elementType.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:
|
||||
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
for i in 0..<ak.kidsLen:
|
||||
if not matchType(c, f[i], ak[i], m): return false
|
||||
return true
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
# say the concept requires 'int|float|string' if the potentialImplementation
|
||||
# says 'int|string' that is good enough.
|
||||
var covered = 0
|
||||
for ff in f.kids:
|
||||
for aa in a.kids:
|
||||
let oldLenB = m.inferred.len
|
||||
let r = matchType(c, ff, aa, m)
|
||||
if r:
|
||||
inc covered
|
||||
break
|
||||
m.inferred.setLen oldLenB
|
||||
|
||||
result = covered >= a.kidsLen
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
else:
|
||||
result = false
|
||||
for ff in f.kids:
|
||||
result = matchType(c, ff, a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.inferred.setLen oldLen
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
result = not matchType(c, f.elementType, 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.returnType, candidate.typ.returnType, 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 = searchInScopesAllCandidatesFilterBy(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)
|
||||
of nkCommentStmt:
|
||||
result = true
|
||||
else:
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
|
||||
## 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.put(a, dest)
|
||||
when logBindings: echo "A bind ", a, " ", dest
|
||||
else:
|
||||
bindings.put(a, b)
|
||||
when logBindings: echo "B bind ", a, " ", b
|
||||
# we have a match, so bind 'arg' itself to 'concpt':
|
||||
bindings.put(concpt, arg)
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
|
||||
# bind even more generic parameters
|
||||
assert invocation.kind == tyGenericInvocation
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
bindings.put(invocation[i], arg[i])
|
||||
@@ -10,10 +10,10 @@
|
||||
# This module handles the conditional symbols.
|
||||
|
||||
import
|
||||
std/strtabs
|
||||
strtabs, platform, strutils, idents
|
||||
|
||||
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
|
||||
@@ -25,7 +25,7 @@ proc undefSymbol*(symbols: StringTableRef; symbol: string) =
|
||||
# result = if isDefined(symbol): gSymbols[symbol] else: nil
|
||||
|
||||
iterator definedSymbolNames*(symbols: StringTableRef): string =
|
||||
for key in keys(symbols):
|
||||
for key, val in pairs(symbols):
|
||||
yield key
|
||||
|
||||
proc countDefinedSymbols*(symbols: StringTableRef): int =
|
||||
@@ -34,139 +34,66 @@ 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("nimlocks") # deadcode
|
||||
defineSymbol("nimnode") # deadcode
|
||||
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") # 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
|
||||
defineSymbol("nimNoZeroExtendMagic") # deadcode
|
||||
defineSymbol("nimMacrosGetNodeId") # deadcode
|
||||
defineSymbol("nimFixedForwardGeneric") # deadcode
|
||||
defineSymbol("nimToOpenArrayCString") # deadcode
|
||||
defineSymbol("nimHasUsed") # deadcode
|
||||
defineSymbol("nimnomagic64") # deadcode
|
||||
defineSymbol("nimNewShiftOps") # deadcode
|
||||
defineSymbol("nimHasCursor") # deadcode
|
||||
defineSymbol("nimAlignPragma") # deadcode
|
||||
defineSymbol("nimHasExceptionsQuery") # deadcode
|
||||
defineSymbol("nimHasIsNamedTuple") # deadcode
|
||||
defineSymbol("nimHashOrdinalFixed") # deadcode
|
||||
defineSymbol("nimHasSinkInference") # deadcode
|
||||
defineSymbol("nimNewIntegerOps") # deadcode
|
||||
defineSymbol("nimHasInvariant") # deadcode
|
||||
|
||||
|
||||
|
||||
for f in Feature:
|
||||
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")
|
||||
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")
|
||||
|
||||
when defined(nimHasLibFFI):
|
||||
# Renaming as we can't conflate input vs output define flags; e.g. this
|
||||
# will report the right thing regardless of whether user adds
|
||||
# `-d:nimHasLibFFI` in his user config.
|
||||
defineSymbol("nimHasLibFFIEnabled") # deadcode
|
||||
|
||||
defineSymbol("nimHasStacktraceMsgs") # deadcode
|
||||
defineSymbol("nimDoesntTrackDefects")
|
||||
defineSymbol("nimHasLentIterators") # deadcode
|
||||
defineSymbol("nimHasDeclaredMagic") # deadcode
|
||||
defineSymbol("nimHasStacktracesModule") # deadcode
|
||||
defineSymbol("nimHasEffectTraitsModule")
|
||||
defineSymbol("nimHasCastPragmaBlocks")
|
||||
defineSymbol("nimHasDeclaredLocs")
|
||||
defineSymbol("nimHasJsBigIntBackend")
|
||||
defineSymbol("nimHasWarningAsError")
|
||||
defineSymbol("nimHasHintAsError")
|
||||
defineSymbol("nimHasSpellSuggest")
|
||||
defineSymbol("nimHasCustomLiterals")
|
||||
defineSymbol("nimHasUnifiedTuple")
|
||||
defineSymbol("nimHasIterable")
|
||||
defineSymbol("nimHasTypeofVoid") # deadcode
|
||||
defineSymbol("nimHasDragonBox") # deadcode
|
||||
defineSymbol("nimHasHintAll")
|
||||
defineSymbol("nimHasTrace")
|
||||
defineSymbol("nimHasEffectsOf")
|
||||
|
||||
defineSymbol("nimHasEnforceNoRaises")
|
||||
defineSymbol("nimHasTopDownInference")
|
||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
||||
defineSymbol("nimHasCstringCase")
|
||||
defineSymbol("nimHasCallsitePragma")
|
||||
|
||||
defineSymbol("nimHasWarnCastSizes") # deadcode
|
||||
defineSymbol("nimHasOutParams")
|
||||
defineSymbol("nimHasSystemRaisesDefect")
|
||||
defineSymbol("nimHasWarnUnnamedBreak")
|
||||
defineSymbol("nimHasGenericDefine")
|
||||
defineSymbol("nimHasDefineAliases")
|
||||
defineSymbol("nimHasWarnBareExcept")
|
||||
defineSymbol("nimHasDup")
|
||||
defineSymbol("nimHasChecksums")
|
||||
defineSymbol("nimHasSendable")
|
||||
defineSymbol("nimAllowNonVarDestructor")
|
||||
defineSymbol("nimHasQuirky")
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimUseStrictDefs")
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
defineSymbol("nimHasWarnStdPrefix")
|
||||
|
||||
defineSymbol("nimHasVtables")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
defineSymbol("nimHasDefaultFloatRoundtrip")
|
||||
|
||||
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
|
||||
72
compiler/cursors.nim
Normal file
72
compiler/cursors.nim
Normal file
@@ -0,0 +1,72 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2019 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import
|
||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
||||
lineinfos, parampatterns
|
||||
|
||||
##[
|
||||
This module implements "cursor" detection. A cursor is a local variable
|
||||
that is used for navigation in a datastructure, it does not "own" the
|
||||
data it aliases but it might update the underlying datastructure.
|
||||
|
||||
Two primary examples for cursors that I have in mind and that are critical
|
||||
for optimization:
|
||||
|
||||
1. Local string variable introduced by ``for x in a``::
|
||||
|
||||
var i = 0
|
||||
while i < a.len:
|
||||
let cursor = a[i]
|
||||
use cursor
|
||||
inc i
|
||||
|
||||
2. Local ``ref`` variable for navigation::
|
||||
|
||||
var cursor = listHead
|
||||
while cursor != nil:
|
||||
use cursor
|
||||
cursor = cursor.next
|
||||
|
||||
Cursors are very interesting for the optimizer because they can be copyMem'ed
|
||||
and don't need a destructor.
|
||||
|
||||
More formally, a cursor is a variable that is set on all paths to
|
||||
a *location* or a proc call that produced a ``lent/var`` type. All statements
|
||||
that come after these assignments MUST not mutate what the cursor aliases.
|
||||
|
||||
Mutations *through* the cursor are allowed if the cursor has ref semantics.
|
||||
|
||||
Look at this complex real world example taken from the compiler itself:
|
||||
|
||||
.. code-block:: Nim
|
||||
|
||||
proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
|
||||
var t = typ
|
||||
while true:
|
||||
if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
|
||||
return t.sym.loc.r
|
||||
|
||||
if t.kind in irrelevantForBackend:
|
||||
t = t.lastSon
|
||||
else:
|
||||
break
|
||||
let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ
|
||||
if typ.loc.r == nil:
|
||||
typ.loc.r = typ.typeName & $sig
|
||||
result = typ.loc.r
|
||||
if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
|
||||
|
||||
Here `t` is a cursor but without a control flow based analysis we are unlikely
|
||||
to detect it.
|
||||
|
||||
]##
|
||||
|
||||
# Araq: I owe you an implementation. For now use the .cursor pragma. :-/
|
||||
@@ -1,72 +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")
|
||||
|
||||
proc enteringDebugSection*() {.exportc, dynlib.} =
|
||||
## Provides a way for native debuggers to enable breakpoints, watchpoints, etc
|
||||
## when code of interest is being compiled.
|
||||
##
|
||||
## Set your debugger to break on entering `nimCompilerIsEnteringDebugSection`
|
||||
## and then execute a desired command.
|
||||
discard
|
||||
|
||||
proc exitingDebugSection*() {.exportc, dynlib.} =
|
||||
## Provides a way for native debuggers to disable breakpoints, watchpoints, etc
|
||||
## when code of interest is no longer being compiled.
|
||||
##
|
||||
## Set your debugger to break on entering `exitingDebugSection`
|
||||
## and then execute a desired command.
|
||||
discard
|
||||
@@ -9,17 +9,11 @@
|
||||
|
||||
# This module implements a dependency file generator.
|
||||
|
||||
import options, ast, ropes, pathutils, msgs, lineinfos
|
||||
|
||||
import modulegraphs
|
||||
|
||||
import std/[os, parseutils]
|
||||
import std/strutils except addf
|
||||
import std/private/globs
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
import
|
||||
os, options, ast, astalgo, msgs, ropes, idents, passes, modulepaths,
|
||||
pathutils
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
type
|
||||
TGen = object of PPassContext
|
||||
@@ -32,69 +26,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:
|
||||
raiseAssert "unreachable"
|
||||
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:
|
||||
sub[^1] = '2'
|
||||
sub.add '/'
|
||||
start = s.find(sub) # /pkgs2
|
||||
if start < 0:
|
||||
return s
|
||||
|
||||
start += sub.len
|
||||
start += skipUntil(s, '/', start)
|
||||
start += 1
|
||||
result = pkgPrefix & s[start..^1]
|
||||
|
||||
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 =
|
||||
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 ..< sonsLen(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 ..< sonsLen(n): discard addDotDependency(c, n.sons[i])
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -104,7 +52,15 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
|
||||
rope(project.splitFile.name), b.dotGraph],
|
||||
changeFileExt(project, "dot"))
|
||||
|
||||
proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
result = PGen(module: module, config: graph.config, graph: graph)
|
||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
var g: PGen
|
||||
new(g)
|
||||
g.module = module
|
||||
g.config = graph.config
|
||||
g.graph = graph
|
||||
if graph.backend == nil:
|
||||
graph.backend = Backend(dotGraph: "")
|
||||
graph.backend = Backend(dotGraph: nil)
|
||||
result = g
|
||||
|
||||
const gendependPass* = makePass(open = myOpen, process = addDotDependency)
|
||||
|
||||
|
||||
811
compiler/dfa.nim
811
compiler/dfa.nim
@@ -9,63 +9,67 @@
|
||||
|
||||
## Data flow analysis for Nim.
|
||||
## We transform the AST into a linear list of instructions first to
|
||||
## make this easier to handle: There are only 3 different branching
|
||||
## make this easier to handle: There are only 2 different branching
|
||||
## instructions: 'goto X' is an unconditional goto, 'fork X'
|
||||
## is a conditional goto (either the next instruction or 'X' can be
|
||||
## taken), 'loop X' is the only jump that jumps back.
|
||||
##
|
||||
## Exhaustive case statements are translated
|
||||
## taken). Exhaustive case statements are translated
|
||||
## so that the last branch is transformed into an 'else' branch.
|
||||
## ``return`` and ``break`` are all covered by 'goto'.
|
||||
##
|
||||
## Control flow through exception handling:
|
||||
## Contrary to popular belief, exception handling doesn't cause
|
||||
## many problems for this DFA representation, ``raise`` is a statement
|
||||
## that ``goes to`` the outer ``finally`` or ``except`` if there is one,
|
||||
## otherwise it is the same as ``return``. Every call is treated as
|
||||
## a call that can potentially ``raise``. However, without a surrounding
|
||||
## ``try`` we don't emit these ``fork ReturnLabel`` instructions in order
|
||||
## to speed up the dataflow analysis passes.
|
||||
##
|
||||
## The data structures and algorithms used here are inspired by
|
||||
## "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, lineinfos, renderer, aliasanalysis
|
||||
import std/private/asciitables
|
||||
import std/intsets
|
||||
import ast, astalgo, types, intsets, tables, msgs, options, lineinfos, renderer
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
from patterns import sameTrees
|
||||
|
||||
type
|
||||
InstrKind* = enum
|
||||
goto, loop, fork, def, use
|
||||
goto, fork, join, def, use
|
||||
Instr* = object
|
||||
n*: PNode
|
||||
case kind*: InstrKind
|
||||
of goto, fork, loop: dest*: int
|
||||
of def, use:
|
||||
n*: PNode # contains the def/use location.
|
||||
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, interestingInstructions: int
|
||||
inCall, inTryStmt: int
|
||||
blocks: seq[TBlock]
|
||||
tryStmtFixups: seq[TPosition]
|
||||
forks: seq[TPosition]
|
||||
owner: PSym
|
||||
root: 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:
|
||||
result = ""
|
||||
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, loop}:
|
||||
if c[i].kind in {goto, fork, join}:
|
||||
jumpTargets.incl(i+c[i].dest)
|
||||
var i = start
|
||||
while i <= last:
|
||||
@@ -76,82 +80,327 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||
case c[i].kind
|
||||
of def, use:
|
||||
result.add renderTree(c[i].n)
|
||||
result.add("\t#")
|
||||
result.add($c[i].n.info.line)
|
||||
result.add("\n")
|
||||
of goto, fork, loop:
|
||||
of goto, fork, join:
|
||||
result.add "L"
|
||||
result.addInt c[i].dest+i
|
||||
result.add("\t#")
|
||||
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): TPosition =
|
||||
proc forkI(c: var Con; n: PNode): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
c.code.add Instr(kind: fork, dest: 0)
|
||||
c.code.add Instr(n: n, kind: fork, dest: 0)
|
||||
c.forks.add result
|
||||
|
||||
proc gotoI(c: var Con): TPosition =
|
||||
proc gotoI(c: var Con; n: PNode): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
c.code.add Instr(kind: goto, dest: 0)
|
||||
c.code.add Instr(n: n, kind: goto, dest: 0)
|
||||
|
||||
proc genLabel(c: Con): TPosition = TPosition(c.code.len)
|
||||
#[
|
||||
|
||||
template checkedDistance(dist): int =
|
||||
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
|
||||
dist
|
||||
Design of join
|
||||
==============
|
||||
|
||||
proc jmpBack(c: var Con, p = TPosition(0)) =
|
||||
c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))
|
||||
block:
|
||||
if cond: break
|
||||
def(x)
|
||||
|
||||
use(x)
|
||||
|
||||
Generates:
|
||||
|
||||
L0: fork lab1
|
||||
join L0 # patched.
|
||||
goto Louter
|
||||
lab1:
|
||||
def x
|
||||
join L0
|
||||
Louter:
|
||||
use x
|
||||
|
||||
|
||||
block outer:
|
||||
while a:
|
||||
while b:
|
||||
if foo:
|
||||
if bar:
|
||||
break outer # --> we need to 'join' every pushed 'fork' here
|
||||
|
||||
|
||||
This works and then our abstract interpretation needs to deal with 'fork'
|
||||
differently. It really causes a split in execution. Two threads are
|
||||
"spawned" and both need to reach the 'join L' instruction. Afterwards
|
||||
the abstract interpretations are joined and execution resumes single
|
||||
threaded.
|
||||
|
||||
|
||||
Abstract Interpretation
|
||||
-----------------------
|
||||
|
||||
proc interpret(pc, state, comesFrom): state =
|
||||
result = state
|
||||
# we need an explicit 'create' instruction (an explicit heap), in order
|
||||
# to deal with 'var x = create(); var y = x; var z = y; destroy(z)'
|
||||
while true:
|
||||
case pc
|
||||
of fork:
|
||||
let a = interpret(pc+1, result, pc)
|
||||
let b = interpret(forkTarget, result, pc)
|
||||
result = a ++ b # ++ is a union operation
|
||||
inc pc
|
||||
of join:
|
||||
if joinTarget == comesFrom: return result
|
||||
else: inc pc
|
||||
of use X:
|
||||
if not result.contains(x):
|
||||
error "variable not initialized " & x
|
||||
inc pc
|
||||
of def X:
|
||||
if not result.contains(x):
|
||||
result.incl X
|
||||
else:
|
||||
error "overwrite of variable causes memory leak " & x
|
||||
inc pc
|
||||
of destroy X:
|
||||
result.excl X
|
||||
|
||||
This is correct but still can lead to false positives:
|
||||
|
||||
proc p(cond: bool) =
|
||||
if cond:
|
||||
new(x)
|
||||
otherThings()
|
||||
if cond:
|
||||
destroy x
|
||||
|
||||
Is not a leak. We should find a way to model *data* flow, not just
|
||||
control flow. One solution is to rewrite the 'if' without a fork
|
||||
instruction. The unstructured aspect can now be easily dealt with
|
||||
the 'goto' and 'join' instructions.
|
||||
|
||||
proc p(cond: bool) =
|
||||
L0: fork Lend
|
||||
new(x)
|
||||
# do not 'join' here!
|
||||
|
||||
Lend:
|
||||
otherThings()
|
||||
join L0 # SKIP THIS FOR new(x) SOMEHOW
|
||||
destroy x
|
||||
join L0 # but here.
|
||||
|
||||
|
||||
|
||||
But if we follow 'goto Louter' we will never come to the join point.
|
||||
We restore the bindings after popping pc from the stack then there
|
||||
"no" problem?!
|
||||
|
||||
|
||||
while cond:
|
||||
prelude()
|
||||
if not condB: break
|
||||
postlude()
|
||||
|
||||
--->
|
||||
var setFlag = true
|
||||
while cond and not setFlag:
|
||||
prelude()
|
||||
if not condB:
|
||||
setFlag = true # BUT: Dependency
|
||||
if not setFlag: # HERE
|
||||
postlude()
|
||||
|
||||
--->
|
||||
var setFlag = true
|
||||
while cond and not setFlag:
|
||||
prelude()
|
||||
if not condB:
|
||||
postlude()
|
||||
setFlag = true
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
while cond:
|
||||
prelude()
|
||||
if more:
|
||||
if not condB: break
|
||||
stuffHere()
|
||||
postlude()
|
||||
|
||||
-->
|
||||
var setFlag = true
|
||||
while cond and not setFlag:
|
||||
prelude()
|
||||
if more:
|
||||
if not condB:
|
||||
setFlag = false
|
||||
else:
|
||||
stuffHere()
|
||||
postlude()
|
||||
else:
|
||||
postlude()
|
||||
|
||||
This is getting complicated. Instead we keep the whole 'join' idea but
|
||||
duplicate the 'join' instructions on breaks and return exits!
|
||||
|
||||
]#
|
||||
|
||||
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)
|
||||
|
||||
proc genLabel(c: Con): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
|
||||
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
|
||||
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()
|
||||
for f in c.blocks[oldLen].breakFixups:
|
||||
c.patch(f[0])
|
||||
for finale in f[1]:
|
||||
c.gen(finale)
|
||||
exits.add c.gotoI()
|
||||
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)
|
||||
|
||||
template forkT(body) =
|
||||
let lab1 = c.forkI()
|
||||
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.)
|
||||
#[
|
||||
while cond:
|
||||
body
|
||||
|
||||
Becomes:
|
||||
|
||||
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.
|
||||
|
||||
So the code to generate is:
|
||||
|
||||
cond
|
||||
fork L4 # F1
|
||||
body
|
||||
cond
|
||||
fork L5 # F2
|
||||
body
|
||||
cond
|
||||
fork L6 # F3
|
||||
body
|
||||
L6:
|
||||
join F3
|
||||
L5:
|
||||
join F2
|
||||
L4:
|
||||
join F1
|
||||
]#
|
||||
if isTrue(n.sons[0]):
|
||||
# 'while true' is an idiom in Nim and so we produce
|
||||
# better code for it:
|
||||
for i in 0..2:
|
||||
withBlock(nil):
|
||||
c.gen(n.sons[1])
|
||||
else:
|
||||
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.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
|
||||
# fork tmp, lab2
|
||||
# body
|
||||
# jmp lab1
|
||||
# lab2:
|
||||
let oldForksLen = c.forks.len
|
||||
let lab1 = c.genLabel
|
||||
withBlock(nil):
|
||||
if isTrue(n.sons[0]):
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, lab1)
|
||||
else:
|
||||
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)
|
||||
|
||||
proc genWhile(c: var Con; n: PNode) =
|
||||
# lab1:
|
||||
# cond, tmp
|
||||
# fork tmp, lab2
|
||||
# body
|
||||
# jmp lab1
|
||||
# lab2:
|
||||
let lab1 = c.genLabel
|
||||
withBlock(nil):
|
||||
if isTrue(n[0]):
|
||||
c.gen(n[1])
|
||||
c.jmpBack(lab1)
|
||||
else:
|
||||
c.gen(n[0])
|
||||
forkT:
|
||||
c.gen(n[1])
|
||||
c.jmpBack(lab1)
|
||||
c.joinI(lab1, n)
|
||||
setLen(c.forks, oldLen)
|
||||
|
||||
proc genIf(c: var Con, n: PNode) =
|
||||
#[
|
||||
@@ -181,33 +430,40 @@ proc genIf(c: var Con, n: PNode) =
|
||||
goto Lend3
|
||||
L3:
|
||||
D
|
||||
goto Lend3 # not eliminated to simplify the join generation
|
||||
Lend3:
|
||||
join F3
|
||||
Lend2:
|
||||
join F2
|
||||
Lend:
|
||||
join F1
|
||||
|
||||
]#
|
||||
let oldLen = c.forks.len
|
||||
var endings: seq[TPosition] = @[]
|
||||
let oldInteresting = c.interestingInstructions
|
||||
let oldLen = c.code.len
|
||||
|
||||
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:
|
||||
c.gen(it.lastSon)
|
||||
endings.add c.gotoI()
|
||||
|
||||
if oldInteresting == c.interestingInstructions:
|
||||
setLen c.code, oldLen
|
||||
else:
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
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):
|
||||
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
|
||||
# fork lab1
|
||||
# asgn dest, b
|
||||
# lab1:
|
||||
c.gen(n[1])
|
||||
forkT:
|
||||
c.gen(n[2])
|
||||
# join F1
|
||||
c.gen(n.sons[1])
|
||||
forkT(n):
|
||||
c.gen(n.sons[2])
|
||||
|
||||
proc genCase(c: var Con; n: PNode) =
|
||||
# if (!expr1) goto lab1;
|
||||
@@ -220,172 +476,253 @@ proc genCase(c: var Con; n: PNode) =
|
||||
# lab2:
|
||||
# elsePart
|
||||
# Lend:
|
||||
let isExhaustive = skipTypes(n[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
|
||||
let isExhaustive = skipTypes(n.sons[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
||||
|
||||
var endings: seq[TPosition] = @[]
|
||||
c.gen(n[0])
|
||||
let oldInteresting = c.interestingInstructions
|
||||
let oldLen = c.code.len
|
||||
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)
|
||||
else:
|
||||
forkT:
|
||||
c.gen(it.lastSon)
|
||||
endings.add c.gotoI()
|
||||
|
||||
if oldInteresting == c.interestingInstructions:
|
||||
setLen c.code, oldLen
|
||||
else:
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
|
||||
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()
|
||||
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) =
|
||||
inc c.interestingInstructions
|
||||
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 preceed 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:
|
||||
c.gen(it.lastSon)
|
||||
endings.add c.gotoI()
|
||||
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) =
|
||||
template genNoReturn(c: var Con; n: PNode) =
|
||||
# leave the graph
|
||||
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
|
||||
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
|
||||
|
||||
proc genRaise(c: var Con; n: PNode) =
|
||||
inc c.interestingInstructions
|
||||
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)
|
||||
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) =
|
||||
inc c.interestingInstructions
|
||||
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}
|
||||
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
|
||||
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
||||
nkAddr, nkHiddenAddr,
|
||||
nkObjDownConv, nkObjUpConv}
|
||||
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
|
||||
|
||||
proc skipTrivials(c: var Con, n: PNode): PNode =
|
||||
proc getRoot(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of PathKinds0 - {nkBracketExpr}:
|
||||
of PathKinds0:
|
||||
result = result[0]
|
||||
of nkBracketExpr:
|
||||
gen(c, result[1])
|
||||
of PathKinds1:
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc skipConvDfa*(n: PNode): PNode =
|
||||
result = n
|
||||
while true:
|
||||
case result.kind
|
||||
of nkObjDownConv, nkObjUpConv:
|
||||
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 and n.sym == c.root:
|
||||
c.code.add Instr(kind: use, n: orig)
|
||||
inc c.interestingInstructions
|
||||
else:
|
||||
gen(c, n)
|
||||
|
||||
proc genDef(c: var Con; orig: PNode) =
|
||||
let n = c.skipTrivials(orig)
|
||||
|
||||
let n = dfa.getRoot(orig)
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
if n.sym == c.root:
|
||||
c.code.add Instr(kind: def, n: orig)
|
||||
inc c.interestingInstructions
|
||||
c.code.add Instr(n: orig, kind: use, sym: if orig != n: nil else: n.sym)
|
||||
|
||||
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, nkHiddenDeref, nkDerefExpr:
|
||||
n = n[0]
|
||||
of nkBracketExpr:
|
||||
let x = n[0]
|
||||
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
|
||||
n = x
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
return false
|
||||
|
||||
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)
|
||||
|
||||
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 nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
|
||||
nkObjDownConv, nkObjUpConv:
|
||||
n = n[0]
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
|
||||
# pointer indirection.
|
||||
n = n[0]
|
||||
return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or
|
||||
n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
|
||||
of nkBracketExpr:
|
||||
let x = n[0]
|
||||
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
|
||||
n = x
|
||||
else:
|
||||
break
|
||||
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
|
||||
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
|
||||
#
|
||||
# let splited = split(move self.root, x)
|
||||
# self.root = merge(splited.lower, splited.greater)
|
||||
#
|
||||
# could be written without the ``move self.root``. However, this would be
|
||||
# wrong! Then the write barrier for the ``self.root`` assignment would
|
||||
# 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 genDef(c: var Con; n: PNode) =
|
||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||
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])
|
||||
if t != nil and i < t.signatureLen and isOutParam(t[i]):
|
||||
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
|
||||
genDef(c, n[i])
|
||||
when false:
|
||||
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]):
|
||||
inc c.interestingInstructions
|
||||
if c.inTryStmt > 0 and canRaise(n[0]):
|
||||
# we generate the instruction sequence:
|
||||
# fork lab1
|
||||
# goto exceptionHandler (except or finally)
|
||||
# lab1:
|
||||
forkT:
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if c.blocks[i].isTryBlock:
|
||||
genBreakOrRaiseAux(c, i, n)
|
||||
break
|
||||
# join F1
|
||||
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
|
||||
@@ -402,45 +739,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:
|
||||
genNoReturn(c)
|
||||
if sfNoReturn in n.sons[0].sym.flags:
|
||||
genNoReturn(c, n)
|
||||
else:
|
||||
genCall(c, n)
|
||||
of nkCharLit..nkNilLit: discard
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
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)
|
||||
@@ -452,40 +783,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: raiseAssert "dfa construction pass requires the elimination of 'defer'"
|
||||
of nkDefer:
|
||||
doAssert false, "dfa construction pass requires the elimination of 'defer'"
|
||||
else: discard
|
||||
|
||||
when false:
|
||||
proc optimizeJumps(c: var ControlFlowGraph) =
|
||||
for i in 0..<c.len:
|
||||
case c[i].kind
|
||||
of goto, fork:
|
||||
var pc = i + c[i].dest
|
||||
if pc < c.len and c[pc].kind == goto:
|
||||
while pc < c.len and c[pc].kind == goto:
|
||||
let newPc = pc + c[pc].dest
|
||||
if newPc > pc:
|
||||
pc = newPc
|
||||
else:
|
||||
break
|
||||
c[i].dest = pc - i
|
||||
of loop, def, use: discard
|
||||
|
||||
proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
|
||||
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
|
||||
## constructs a control flow graph for ``body``.
|
||||
var c = Con(code: @[], blocks: @[], owner: s, root: root)
|
||||
withBlock(s):
|
||||
gen(c, body)
|
||||
if root.kind == skResult:
|
||||
genImplicitReturn(c)
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result = c.code # will move
|
||||
else:
|
||||
shallowCopy(result, c.code)
|
||||
when false:
|
||||
optimizeJumps result
|
||||
var c = Con(code: @[], blocks: @[], owner: s)
|
||||
gen(c, body)
|
||||
genImplicitReturn(c)
|
||||
shallowCopy(result, c.code)
|
||||
|
||||
1973
compiler/docgen.nim
1973
compiler/docgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,8 @@
|
||||
# semantic checking.
|
||||
|
||||
import
|
||||
options, ast, msgs, docgen, lineinfos, pathutils, packages
|
||||
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos,
|
||||
pathutils
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
@@ -22,43 +23,39 @@ 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)
|
||||
except IOError:
|
||||
discard
|
||||
|
||||
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
result = nil
|
||||
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
closeImpl:
|
||||
writeOutput(g.doc, useWarning, groupedToc)
|
||||
writeOutput(g.doc, useWarning)
|
||||
|
||||
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
result = nil
|
||||
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
closeImpl:
|
||||
writeOutputJson(g.doc, useWarning)
|
||||
|
||||
proc processNode*(c: PPassContext, n: PNode): PNode =
|
||||
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
if shouldProcess(g):
|
||||
generateDoc(g.doc, n, n, g.config)
|
||||
generateDoc(g.doc, n, n)
|
||||
|
||||
proc processNodeJson*(c: PPassContext, n: PNode): PNode =
|
||||
proc processNodeJson(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
if shouldProcess(g):
|
||||
generateJson(g.doc, n, g.config, false)
|
||||
generateJson(g.doc, n, false)
|
||||
|
||||
template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
var g: PGen
|
||||
@@ -66,15 +63,20 @@ template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
g.module = module
|
||||
g.config = graph.config
|
||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
||||
graph.cache, graph.config, ext, module, hasToc = true)
|
||||
graph.cache, graph.config, ext)
|
||||
d.hasToc = true
|
||||
g.doc = d
|
||||
result = g
|
||||
|
||||
proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
myOpenImpl(HtmlExt)
|
||||
|
||||
proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
myOpenImpl(TexExt)
|
||||
|
||||
proc openJson*(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 docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
|
||||
close = closeJson)
|
||||
|
||||
proc finishDoc2Pass*(project: string) =
|
||||
discard
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
|
||||
import ast, idents, lineinfos, modulegraphs, magicsys
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), t.owner, info)
|
||||
|
||||
|
||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), result, info)
|
||||
dest.typ = t
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
|
||||
res.typ = getSysType(g, info, tyString)
|
||||
|
||||
result.typ = newType(tyProc, 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
|
||||
|
||||
@@ -26,60 +22,52 @@ 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
|
||||
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
|
||||
caseStmt.add newTree(nkOfBranch, newSymNode(field),
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
||||
#newIntTypeNode(nkIntLit, field.position, t)
|
||||
|
||||
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:
|
||||
result = nil
|
||||
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.baseClass != nil:
|
||||
result = searchObjCase(t.baseClass.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"), idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), 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"), idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), result, info)
|
||||
res.typ = getSysType(g, info, tyUInt8)
|
||||
|
||||
result.typ = newType(tyProc, 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
|
||||
|
||||
@@ -87,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)))
|
||||
@@ -100,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,85 +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, types
|
||||
import std/strutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
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
|
||||
var idgen = idGeneratorForPackage(-1'i32)
|
||||
result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, 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
|
||||
var idgen = idGeneratorForPackage(-1'i32)
|
||||
result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, 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,20 +9,13 @@
|
||||
|
||||
## This file implements the FFI part of the evaluator for Nim code.
|
||||
|
||||
import ast, types, options, msgs, lineinfos
|
||||
from std/os import getAppFilename
|
||||
import libffi/libffi
|
||||
|
||||
import std/[tables, dynlib]
|
||||
import ast, 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:
|
||||
@@ -34,15 +27,14 @@ var
|
||||
gDllCache = initTable[string, LibHandle]()
|
||||
|
||||
when defined(windows):
|
||||
var gExeHandle = loadLib(getAppFilename())
|
||||
var gExeHandle = loadLib(os.getAppFilename())
|
||||
else:
|
||||
var gExeHandle = loadLib()
|
||||
|
||||
proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
|
||||
result = nil
|
||||
if dll in cache:
|
||||
return cache[dll]
|
||||
var libs: seq[string] = @[]
|
||||
var libs: seq[string]
|
||||
libCandidates(dll, libs)
|
||||
for c in libs:
|
||||
result = loadLib(c)
|
||||
@@ -54,35 +46,38 @@ 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 = nil
|
||||
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
|
||||
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
|
||||
var theAddr: pointer
|
||||
if (lib.isNil or lib.kind == libHeader) and not gExehandle.isNil:
|
||||
# first try this exe itself:
|
||||
theAddr = gExeHandle.symAddr(name.cstring)
|
||||
theAddr = gExehandle.symAddr(name)
|
||||
# then try libc:
|
||||
if theAddr.isNil:
|
||||
let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
|
||||
theAddr = dllhandle.symAddr(name.cstring)
|
||||
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.cstring)
|
||||
if theAddr.isNil: globalError(conf, sym.info,
|
||||
"cannot import symbol: " & name & " from " & libPathMsg)
|
||||
result.intVal = cast[int](theAddr)
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
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
|
||||
@@ -95,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.skipModifier)
|
||||
result = mapType(conf, t.sons[0])
|
||||
else:
|
||||
result = nil
|
||||
# too risky:
|
||||
@@ -107,17 +102,16 @@ 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:
|
||||
result = default(TABI)
|
||||
globalError(conf, info, "cannot map calling convention to FFI")
|
||||
|
||||
template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
|
||||
template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
|
||||
template 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[int](x) + y)
|
||||
cast[pointer](cast[ByteAddress](x) + y)
|
||||
|
||||
proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
|
||||
## computes the size of the blob
|
||||
@@ -126,18 +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.elementType)
|
||||
result = sizeof(pointer) + packSize(conf, v.sons[0], typ.lastSon)
|
||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||
result = packSize(conf, v, typ.skipModifier)
|
||||
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.elementType)
|
||||
else:
|
||||
result = 0
|
||||
result = v.len * packSize(conf, v.sons[0], typ.sons[1])
|
||||
else:
|
||||
result = getSize(conf, typ).int
|
||||
|
||||
@@ -146,34 +138,32 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
|
||||
proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
result = nil
|
||||
for i in 0..<n.len:
|
||||
result = getField(conf, n[i], position)
|
||||
for i in 0 ..< sonsLen(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 ..< sonsLen(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:
|
||||
if n.sym.position == position: result = n.sym
|
||||
else: result = nil
|
||||
else: result = nil
|
||||
else: discard
|
||||
|
||||
proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
|
||||
internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
|
||||
# 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) ..< sonsLen(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)
|
||||
@@ -185,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)
|
||||
@@ -213,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
|
||||
@@ -234,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.elementType, 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.elementType)
|
||||
for i in 0..<v.len:
|
||||
pack(conf, v[i], typ.elementType, 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.skipModifier, res)
|
||||
pack(conf, v, typ.sons[0], res)
|
||||
else:
|
||||
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
|
||||
@@ -255,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 ..< sonsLen(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
|
||||
@@ -275,7 +265,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
# the nkPar node:
|
||||
if n.isNil:
|
||||
result = newNode(nkTupleConstr)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
if typ.n.isNil:
|
||||
internalError(conf, "cannot unpack unnamed tuple")
|
||||
unpackObjectAdd(conf, x, typ.n, result)
|
||||
@@ -285,28 +275,28 @@ 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) ..< sonsLen(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:
|
||||
result = newNode(nkBracket)
|
||||
result.typ() = typ
|
||||
newSeq(result.sons, lengthOrd(conf, typ).toInt)
|
||||
result.typ = typ
|
||||
newSeq(result.sons, lengthOrd(conf, typ).int)
|
||||
else:
|
||||
result = n
|
||||
if result.kind != nkBracket:
|
||||
globalError(conf, n.info, "cannot map value from FFI")
|
||||
let baseSize = getSize(conf, typ.elementType)
|
||||
for i in 0..<result.len:
|
||||
result[i] = unpack(conf, x +! i * baseSize, typ.elementType, 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
|
||||
@@ -319,7 +309,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template aw(k, v, field: untyped): untyped =
|
||||
if n.isNil:
|
||||
result = newNode(k)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
else:
|
||||
# check we have the right field:
|
||||
result = n
|
||||
@@ -333,12 +323,12 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template setNil() =
|
||||
if n.isNil:
|
||||
result = newNode(nkNilLit)
|
||||
result.typ() = typ
|
||||
result.typ = typ
|
||||
else:
|
||||
reset n[]
|
||||
result = n
|
||||
result[] = TNode(kind: nkNilLit)
|
||||
result.typ() = typ
|
||||
result.kind = nkNilLit
|
||||
result.typ = typ
|
||||
|
||||
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
|
||||
template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
|
||||
@@ -364,7 +354,6 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
|
||||
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
|
||||
else:
|
||||
result = nil
|
||||
globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
|
||||
of tyFloat: awf(nkFloatLit, rd(float, x))
|
||||
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
||||
@@ -378,25 +367,24 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
# in their unboxed representation so nothing it to be unpacked:
|
||||
result = n
|
||||
else:
|
||||
awi(nkPtrLit, cast[int](p))
|
||||
awi(nkPtrLit, cast[ByteAddress](p))
|
||||
of tyPtr, tyRef, tyVar, tyLent:
|
||||
let p = rd(pointer, x)
|
||||
if p.isNil:
|
||||
setNil()
|
||||
elif n == nil or n.kind == nkPtrLit:
|
||||
awi(nkPtrLit, cast[int](p))
|
||||
awi(nkPtrLit, cast[ByteAddress](p))
|
||||
elif n != nil and n.len == 1:
|
||||
internalAssert(conf, n.kind == nkRefTy)
|
||||
n[0] = unpack(conf, p, typ.elementType, n[0])
|
||||
n.sons[0] = unpack(conf, p, typ.lastSon, n.sons[0])
|
||||
result = n
|
||||
else:
|
||||
result = nil
|
||||
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
of tyObject, tyTuple:
|
||||
result = unpackObject(conf, x, typ, n)
|
||||
of tyArray:
|
||||
result = unpackArray(conf, x, typ, n)
|
||||
of tyCstring, tyString:
|
||||
of tyCString, tyString:
|
||||
let p = rd(cstring, x)
|
||||
if p.isNil:
|
||||
setNil()
|
||||
@@ -405,15 +393,14 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
of tyNil:
|
||||
setNil()
|
||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
||||
result = unpack(conf, x, typ.skipModifier, n)
|
||||
result = unpack(conf, x, typ.lastSon, n)
|
||||
else:
|
||||
# XXX what to do with 'array' here?
|
||||
result = nil
|
||||
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
|
||||
proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
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
|
||||
@@ -427,46 +414,46 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
# cast through a pointer needs a new inner object:
|
||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
||||
else: x.copyTree
|
||||
y.typ() = x.typ
|
||||
y.typ = x.typ
|
||||
result = unpack(conf, a, destTyp, y)
|
||||
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 = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
var cif: TCif
|
||||
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.returnType), sig) != OK:
|
||||
mapType(conf, typ.sons[0]), sig) != OK:
|
||||
globalError(conf, call.info, "error in FFI call")
|
||||
|
||||
var args: ArgList = default(ArgList)
|
||||
let fn = cast[pointer](call[0].intVal)
|
||||
for i in 1..<call.len:
|
||||
var t = call[i].typ
|
||||
args[i-1] = alloc0(packSize(conf, call[i], t))
|
||||
pack(conf, call[i], t, args[i-1])
|
||||
let retVal = if isEmptyType(typ.returnType): pointer(nil)
|
||||
else: alloc(getSize(conf, typ.returnType).int)
|
||||
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.returnType, 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,
|
||||
@@ -474,40 +461,40 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
info: TLineInfo): PNode =
|
||||
internalAssert conf, fn.kind == nkPtrLit
|
||||
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
var cif: TCif
|
||||
var sig: 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]
|
||||
args[i+start].typ() = aTyp
|
||||
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 = default(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]
|
||||
|
||||
@@ -9,43 +9,28 @@
|
||||
|
||||
## Template evaluation engine. Now hygienic.
|
||||
|
||||
import options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
|
||||
import std/strutils
|
||||
import
|
||||
strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
|
||||
lineinfos
|
||||
|
||||
type
|
||||
TemplCtx = object
|
||||
owner, genSymOwner: PSym
|
||||
instLines: bool # use the instantiation lines numbers
|
||||
isDeclarative: bool
|
||||
mapping: SymMapping # every gensym'ed symbol needs to be mapped to some
|
||||
# new symbol
|
||||
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
|
||||
# new symbol
|
||||
config: ConfigRef
|
||||
ic: IdentCache
|
||||
instID: int
|
||||
idgen: IdGenerator
|
||||
|
||||
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
||||
result = copyNode(a)
|
||||
if ctx.instLines: setInfoRecursive(result, b.info)
|
||||
if ctx.instLines: result.info = b.info
|
||||
|
||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
template handleParam(param) =
|
||||
let x = param
|
||||
if x.kind == nkArgList:
|
||||
for y in items(x): result.add(y)
|
||||
elif nfDefaultRefsParam in x.flags:
|
||||
# value of default param needs to be evaluated like template body
|
||||
# if it contains other template params
|
||||
var res: PNode
|
||||
if isAtom(x):
|
||||
res = newNodeI(nkPar, x.info)
|
||||
evalTemplateAux(x, actual, c, res)
|
||||
if res.len == 1: res = res[0]
|
||||
else:
|
||||
res = copyNode(x)
|
||||
for i in 0..<x.safeLen:
|
||||
evalTemplateAux(x[i], actual, c, res)
|
||||
result.add res
|
||||
else:
|
||||
result.add copyTree(x)
|
||||
|
||||
@@ -53,27 +38,22 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
of nkSym:
|
||||
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]
|
||||
if s.kind == skParam and sfGenSym notin s.flags:
|
||||
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.signatureLen + 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 = idTableGet(c.mapping, s)
|
||||
var x = PSym(idTableGet(c.mapping, s))
|
||||
if x == nil:
|
||||
x = copySym(s, c.idgen)
|
||||
x = copySym(s)
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
setOwner(x, nil) # c.genSymOwner
|
||||
x.owner = nil # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
if sfGenSym in s.flags:
|
||||
# TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
|
||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
|
||||
if c.instLines: actual.info else: templ.info)
|
||||
else:
|
||||
result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
|
||||
result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
|
||||
else:
|
||||
result.add copyNode(c, templ, actual)
|
||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||
@@ -84,8 +64,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 ..< sonsLen(templ):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
else:
|
||||
result.add newNodeI(nkEmpty, templ.info)
|
||||
@@ -97,14 +77,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 ..< sonsLen(templ):
|
||||
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||
result.add res
|
||||
if isDeclarative: c.isDeclarative = false
|
||||
|
||||
const
|
||||
@@ -130,7 +106,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
|
||||
# now that we have working untyped parameters.
|
||||
genericParams = if fromHlo: 0
|
||||
else: s.ast[genericParamsPos].len
|
||||
expectedRegularParams = s.typ.paramsLen
|
||||
expectedRegularParams = s.typ.len-1
|
||||
givenRegularParams = totalParams - genericParams
|
||||
if givenRegularParams < 0: givenRegularParams = 0
|
||||
|
||||
@@ -142,22 +118,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]
|
||||
# add any generic paramaters
|
||||
for i in 1 .. genericParams:
|
||||
result.addSon n.sons[givenRegularParams + i]
|
||||
|
||||
# to prevent endless recursion in template instantiation
|
||||
const evalTemplateLimit* = 1000
|
||||
@@ -175,20 +151,17 @@ 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)
|
||||
d.add newSymNode(sym, info)
|
||||
result.add d
|
||||
result.add res
|
||||
result.typ() = res.typ
|
||||
result.typ = res.typ
|
||||
|
||||
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
conf: ConfigRef;
|
||||
ic: IdentCache; instID: ref int;
|
||||
idgen: IdGenerator;
|
||||
fromHlo=false): PNode =
|
||||
conf: ConfigRef; fromHlo=false): PNode =
|
||||
inc(conf.evalTemplateCounter)
|
||||
if conf.evalTemplateCounter > evalTemplateLimit:
|
||||
globalError(conf, n.info, errTemplateInstantiationTooNested)
|
||||
@@ -196,21 +169,18 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
|
||||
# replace each param by the corresponding node:
|
||||
var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
|
||||
var ctx = TemplCtx(owner: tmpl,
|
||||
genSymOwner: genSymOwner,
|
||||
config: conf,
|
||||
ic: ic,
|
||||
mapping: initSymMapping(),
|
||||
instID: instID[],
|
||||
idgen: idgen
|
||||
)
|
||||
var ctx: TemplCtx
|
||||
ctx.owner = tmpl
|
||||
ctx.genSymOwner = genSymOwner
|
||||
ctx.config = conf
|
||||
initIdTable(ctx.mapping)
|
||||
|
||||
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}))
|
||||
@@ -218,13 +188,11 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
result = copyNode(body)
|
||||
ctx.instLines = sfCallsite in tmpl.flags
|
||||
if ctx.instLines:
|
||||
setInfoRecursive(result, n.info)
|
||||
for i in 0..<body.safeLen:
|
||||
evalTemplateAux(body[i], args, ctx, result)
|
||||
result.info = n.info
|
||||
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[]
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2023 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import lineinfos, ast, types
|
||||
|
||||
proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int =
|
||||
result = 0
|
||||
for i in 1 ..< obj.len:
|
||||
for j in 0 .. obj[i].len - 2:
|
||||
if obj[i][j].kind == nkRange:
|
||||
let x = getOrdValue(obj[i][j][0])
|
||||
let y = getOrdValue(obj[i][j][1])
|
||||
if branch >= x and branch <= y:
|
||||
return i
|
||||
elif getOrdValue(obj[i][j]) == branch:
|
||||
return i
|
||||
if obj[i].len == 1:
|
||||
# else branch
|
||||
return i
|
||||
return 1
|
||||
|
||||
template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t)
|
||||
|
||||
proc expandDefault*(t: PType; info: TLineInfo): PNode
|
||||
|
||||
proc expandField(s: PSym; info: TLineInfo): PNode =
|
||||
result = newNodeIT(nkExprColonExpr, info, s.typ)
|
||||
result.add newSymNode(s)
|
||||
result.add expandDefault(s.typ, info)
|
||||
|
||||
proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
expandDefaultN(n[i], info, res)
|
||||
of nkRecCase:
|
||||
res.add expandField(n[0].sym, info)
|
||||
var branch = Zero
|
||||
let constOrNil = n[0].sym.astdef
|
||||
if constOrNil != nil:
|
||||
branch = getOrdValue(constOrNil)
|
||||
|
||||
let selectedBranch = caseObjDefaultBranch(n, branch)
|
||||
let b = lastSon(n[selectedBranch])
|
||||
expandDefaultN b, info, res
|
||||
of nkSym:
|
||||
res.add expandField(n.sym, info)
|
||||
else:
|
||||
discard
|
||||
|
||||
proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
|
||||
if t.baseClass != nil:
|
||||
expandDefaultObj(t.baseClass, info, res)
|
||||
expandDefaultN(t.n, info, res)
|
||||
|
||||
proc expandDefault(t: PType; info: TLineInfo): PNode =
|
||||
case t.kind
|
||||
of tyInt: result = newZero(t, info, nkIntLit)
|
||||
of tyInt8: result = newZero(t, info, nkInt8Lit)
|
||||
of tyInt16: result = newZero(t, info, nkInt16Lit)
|
||||
of tyInt32: result = newZero(t, info, nkInt32Lit)
|
||||
of tyInt64: result = newZero(t, info, nkInt64Lit)
|
||||
of tyUInt: result = newZero(t, info, nkUIntLit)
|
||||
of tyUInt8: result = newZero(t, info, nkUInt8Lit)
|
||||
of tyUInt16: result = newZero(t, info, nkUInt16Lit)
|
||||
of tyUInt32: result = newZero(t, info, nkUInt32Lit)
|
||||
of tyUInt64: result = newZero(t, info, nkUInt64Lit)
|
||||
of tyFloat: result = newZero(t, info, nkFloatLit)
|
||||
of tyFloat32: result = newZero(t, info, nkFloat32Lit)
|
||||
of tyFloat64: result = newZero(t, info, nkFloat64Lit)
|
||||
of tyFloat128: result = newZero(t, info, nkFloat64Lit)
|
||||
of tyChar: result = newZero(t, info, nkCharLit)
|
||||
of tyBool: result = newZero(t, info, nkIntLit)
|
||||
of tyEnum:
|
||||
# Could use low(T) here to finally fix old language quirks
|
||||
result = newZero(t, info, nkIntLit)
|
||||
of tyRange:
|
||||
# Could use low(T) here to finally fix old language quirks
|
||||
result = expandDefault(skipModifier t, info)
|
||||
of tyVoid: result = newZero(t, info, nkEmpty)
|
||||
of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
|
||||
result = expandDefault(t.skipModifier, info)
|
||||
of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
|
||||
if t.hasElementType:
|
||||
result = expandDefault(t.skipModifier, info)
|
||||
else:
|
||||
result = newZero(t, info, nkEmpty)
|
||||
of tyFromExpr:
|
||||
if t.n != nil and t.n.typ != nil:
|
||||
result = expandDefault(t.n.typ, info)
|
||||
else:
|
||||
result = newZero(t, info, nkEmpty)
|
||||
of tyArray:
|
||||
result = newZero(t, info, nkBracket)
|
||||
let n = toInt64(lengthOrd(nil, t))
|
||||
for i in 0..<n:
|
||||
result.add expandDefault(t.elementType, info)
|
||||
of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
|
||||
result = newZero(t, info, nkNilLit)
|
||||
of tyVar, tyLent:
|
||||
let e = t.elementType
|
||||
if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
# skip the modifier, `var openArray` is a (ptr, len) pair too:
|
||||
result = expandDefault(e, info)
|
||||
else:
|
||||
result = newZero(e, info, nkNilLit)
|
||||
of tySet:
|
||||
result = newZero(t, info, nkCurly)
|
||||
of tyObject:
|
||||
result = newNodeIT(nkObjConstr, info, t)
|
||||
result.add newNodeIT(nkType, info, t)
|
||||
expandDefaultObj(t, info, result)
|
||||
of tyTuple:
|
||||
result = newZero(t, info, nkTupleConstr)
|
||||
for it in t.kids:
|
||||
result.add expandDefault(it, info)
|
||||
of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
|
||||
result = newZero(t, info, nkBracket)
|
||||
of tyString:
|
||||
result = newZero(t, info, nkStrLit)
|
||||
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
|
||||
tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
|
||||
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
|
||||
result = newZero(t, info, nkEmpty) # bug indicator
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,10 +10,8 @@
|
||||
# This module implements Nim's standard template filter.
|
||||
|
||||
import
|
||||
llstream, ast, msgs, options,
|
||||
filters, lineinfos, pathutils
|
||||
|
||||
import std/strutils
|
||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||
renderer, filters, lineinfos, pathutils
|
||||
|
||||
type
|
||||
TParseState = enum
|
||||
@@ -24,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 ouput will be parsed by pnimsyn
|
||||
subsChar, nimDirective: char
|
||||
emit, conc, toStr: string
|
||||
curly, bracket, par: int
|
||||
@@ -77,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)
|
||||
@@ -201,22 +199,24 @@ proc parseLine(p: var TTmplParser) =
|
||||
inc(j)
|
||||
llStreamWrite(p.outp, "\\n\"")
|
||||
|
||||
proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
|
||||
call: PNode): PLLStream =
|
||||
var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
|
||||
outp: llStreamOpen(""), inp: stdin,
|
||||
subsChar: charArg(conf, call, "subschar", 1, '$'),
|
||||
nimDirective: charArg(conf, call, "metachar", 2, '#'),
|
||||
emit: strArg(conf, call, "emit", 3, "result.add"),
|
||||
conc: strArg(conf, call, "conc", 4, " & "),
|
||||
toStr: strArg(conf, call, "tostring", 5, "$"),
|
||||
x: newStringOfCap(120)
|
||||
)
|
||||
proc filterTmpl*(stdin: PLLStream, filename: AbsoluteFile,
|
||||
call: PNode; conf: ConfigRef): PLLStream =
|
||||
var p: TTmplParser
|
||||
p.config = conf
|
||||
p.info = newLineInfo(conf, filename, 0, 0)
|
||||
p.outp = llStreamOpen("")
|
||||
p.inp = stdin
|
||||
p.subsChar = charArg(conf, call, "subschar", 1, '$')
|
||||
p.nimDirective = charArg(conf, call, "metachar", 2, '#')
|
||||
p.emit = strArg(conf, call, "emit", 3, "result.add")
|
||||
p.conc = strArg(conf, call, "conc", 4, " & ")
|
||||
p.toStr = strArg(conf, call, "tostring", 5, "$")
|
||||
p.x = newStringOfCap(120)
|
||||
# do not process the first line which contains the directive:
|
||||
if llStreamReadLine(p.inp, p.x):
|
||||
inc p.info.line
|
||||
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
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
# This module implements Nim's simple filters and helpers for filters.
|
||||
|
||||
import
|
||||
llstream, idents, ast, msgs, options,
|
||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||
renderer, pathutils
|
||||
|
||||
import std/strutils
|
||||
|
||||
proc invalidPragma(conf: ConfigRef; n: PNode) =
|
||||
localError(conf, n.info,
|
||||
"'$1' not allowed here" % renderTree(n, {renderNoComments}))
|
||||
@@ -22,39 +20,32 @@ 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 ..< sonsLen(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)
|
||||
if x == nil: result = default
|
||||
elif x.kind == nkCharLit: result = chr(int(x.intVal))
|
||||
else:
|
||||
result = default(char)
|
||||
invalidPragma(conf, n)
|
||||
else: invalidPragma(conf, n)
|
||||
|
||||
proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
|
||||
var x = getArg(conf, n, name, pos)
|
||||
if x == nil: result = default
|
||||
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
|
||||
else:
|
||||
result = ""
|
||||
invalidPragma(conf, n)
|
||||
else: invalidPragma(conf, n)
|
||||
|
||||
proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
|
||||
var x = getArg(conf, n, name, pos)
|
||||
if x == nil: result = default
|
||||
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
|
||||
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
|
||||
else:
|
||||
result = false
|
||||
invalidPragma(conf, n)
|
||||
else: invalidPragma(conf, n)
|
||||
|
||||
proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
|
||||
var pattern = strArg(conf, call, "startswith", 1, "")
|
||||
@@ -64,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)
|
||||
@@ -72,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)
|
||||
@@ -9,14 +9,8 @@
|
||||
|
||||
## Module that implements ``gorge`` for the compiler.
|
||||
|
||||
import msgs, options, lineinfos, pathutils
|
||||
|
||||
import std/[os, osproc, streams]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
import msgs, std / sha1, os, osproc, streams, strutils, options,
|
||||
lineinfos, pathutils
|
||||
|
||||
proc readOutput(p: Process): (string, int) =
|
||||
result[0] = ""
|
||||
@@ -30,11 +24,10 @@ proc readOutput(p: Process): (string, int) =
|
||||
|
||||
proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
|
||||
let workingDir = parentDir(toFullPath(conf, info))
|
||||
result = ("", 0)
|
||||
if cache.len > 0:
|
||||
let h = secureHash(cmd & "\t" & input & "\t" & cache)
|
||||
let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
|
||||
var f: File = default(File)
|
||||
var f: File
|
||||
if optForceFullMake notin conf.globalOptions and open(f, filename):
|
||||
result = (f.readAll, 0)
|
||||
f.close
|
||||
@@ -47,17 +40,12 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
p.close()
|
||||
readSuccessful = true
|
||||
# only cache successful runs:
|
||||
if result[1] == 0:
|
||||
writeFile(filename, result[0])
|
||||
except IOError, OSError:
|
||||
if not readSuccessful:
|
||||
when defined(nimLegacyGorgeErrors):
|
||||
result = ("", -1)
|
||||
else:
|
||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
||||
if not readSuccessful: result = ("", -1)
|
||||
else:
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
@@ -66,9 +54,5 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
result = p.readOutput
|
||||
p.close()
|
||||
except IOError, OSError:
|
||||
when defined(nimLegacyGorgeErrors):
|
||||
result = ("", -1)
|
||||
else:
|
||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
||||
result = ("", -1)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,21 +8,18 @@
|
||||
#
|
||||
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
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
|
||||
# aweful to semcheck before macro invocation, so we don't and treat
|
||||
# templates and macros as immediate in this context.
|
||||
var rule: string =
|
||||
if c.config.hasHint(hintPattern):
|
||||
renderTree(n, {renderNoComments})
|
||||
else:
|
||||
""
|
||||
let s = n[0].sym
|
||||
var rule: string
|
||||
if optHints in c.config.options and hintPattern in c.config.notes:
|
||||
rule = renderTree(n, {renderNoComments})
|
||||
let s = n.sons[0].sym
|
||||
case s.kind
|
||||
of skMacro:
|
||||
result = semMacroExpr(c, n, orig, s)
|
||||
@@ -30,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}) & "'")
|
||||
|
||||
@@ -53,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)
|
||||
@@ -70,18 +67,18 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
# already processed (special cases in semstmts.nim)
|
||||
result = n
|
||||
else:
|
||||
if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
|
||||
n[0].kind == nkSym and
|
||||
{sfGlobal, sfPure} <= n[0].sym.flags:
|
||||
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
|
||||
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):
|
||||
@@ -89,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,178 +0,0 @@
|
||||
## A BiTable is a table that can be seen as an optimized pair
|
||||
## of `(Table[LitId, Val], Table[Val, LitId])`.
|
||||
|
||||
import std/hashes
|
||||
import rodfiles
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
LitId* = distinct uint32
|
||||
|
||||
BiTable*[T] = object
|
||||
vals: seq[T] # indexed by LitId
|
||||
keys: seq[LitId] # indexed by hash(val)
|
||||
|
||||
proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[])
|
||||
|
||||
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 = 1
|
||||
|
||||
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)
|
||||
|
||||
proc sizeOnDisc*(t: BiTable[string]): int =
|
||||
result = 4
|
||||
for x in t.vals:
|
||||
result += x.len + 4
|
||||
result += t.keys.len * sizeof(LitId)
|
||||
|
||||
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,180 +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]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
pathutils, extccomp, msgs, modulepaths]
|
||||
|
||||
import packed_ast, ic, dce, rodfiles
|
||||
|
||||
proc unpackTree(g: ModuleGraph; thisModule: int;
|
||||
tree: PackedTree; n: NodePos): PNode =
|
||||
var decoder = initPackedDecoder(g.config, g.cache)
|
||||
result = loadNodes(decoder, g.packed, thisModule, tree, n)
|
||||
|
||||
proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
|
||||
if g.backend == nil:
|
||||
g.backend = cgendata.newModuleList(g)
|
||||
assert g.backend != nil
|
||||
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
|
||||
bmod.idgen = idgenFromLoadedModule(m)
|
||||
|
||||
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
|
||||
var bmod = BModuleList(g.backend).modules[m.module.position]
|
||||
assert bmod != nil
|
||||
bmod.flags.incl useAliveDataFromDce
|
||||
bmod.alive = move alive[m.module.position]
|
||||
|
||||
for p in allNodes(m.fromDisk.topLevel):
|
||||
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
|
||||
cgen.genTopLevelStmt(bmod, n)
|
||||
|
||||
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
for disp in getDispatchers(g):
|
||||
genProcAux(bmod, disp)
|
||||
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..<len(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..<len(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..<len(g.packed):
|
||||
if i != mainModuleIdx:
|
||||
genPackedModule(g, i, alive)
|
||||
if mainModuleIdx >= 0:
|
||||
genPackedModule(g, mainModuleIdx, alive)
|
||||
@@ -1,169 +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]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ".." / [ast, options, lineinfos, types]
|
||||
|
||||
import packed_ast, ic, bitabs
|
||||
|
||||
type
|
||||
AliveSyms* = seq[IntSet]
|
||||
AliveContext* = object ## Purpose is to fill the 'alive' field.
|
||||
stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive.
|
||||
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
|
||||
thisModule: int ## The module we're currently analysing for DCE.
|
||||
alive: AliveSyms ## The final result of our computation.
|
||||
options: TOptions
|
||||
compilerProcs: Table[string, (int, int32)]
|
||||
|
||||
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
|
||||
## "Exported to C" procs are special (these are marked with '.exportc') because these
|
||||
## must not be optimized away!
|
||||
let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId]
|
||||
let flags = symPtr.flags
|
||||
# due to a bug/limitation in the lambda lifting, unused inner procs
|
||||
# are not transformed correctly; issue (#411). However, the whole purpose here
|
||||
# is to eliminate unused procs. So there is no special logic required for this case.
|
||||
if sfCompileTime notin flags:
|
||||
if ({sfExportc, sfCompilerProc} * flags != {}) or
|
||||
(symPtr.kind == skMethod):
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
# 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)
|
||||
else:
|
||||
result = false
|
||||
|
||||
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, tree[n].soperand)
|
||||
of nkModuleRef:
|
||||
let (n1, n2) = sons2(tree, n)
|
||||
assert n1.kind == nkNone
|
||||
assert n2.kind == nkNone
|
||||
let m = n1.litId
|
||||
let item = tree[n2].soperand
|
||||
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 = tree[n.firstSon].soperand
|
||||
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(len(g)-1, 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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user