Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07fb6df6bc |
1583 changed files with 41115 additions and 67685 deletions
37
.builds/freebsd.yml
Normal file
37
.builds/freebsd.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||||
|
|
||||||
|
# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd
|
||||||
|
image: freebsd/latest
|
||||||
|
packages:
|
||||||
|
- databases/sqlite3
|
||||||
|
- devel/boehm-gc-threaded
|
||||||
|
- devel/pcre
|
||||||
|
- devel/sdl20
|
||||||
|
- devel/sfml
|
||||||
|
- www/node
|
||||||
|
- devel/gmake
|
||||||
|
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- https://github.com/nim-lang/Nim
|
||||||
|
environment:
|
||||||
|
NIM_TESTAMENT_BATCH: "0_1"
|
||||||
|
CC: /usr/bin/clang
|
||||||
|
tasks:
|
||||||
|
- setup: |
|
||||||
|
set -e
|
||||||
|
cd Nim
|
||||||
|
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||||
|
$nim_csources c --skipUserCfg --skipParentCfg koch
|
||||||
|
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||||
|
- test: |
|
||||||
|
set -e
|
||||||
|
cd Nim
|
||||||
|
if ! ./koch runCI; then
|
||||||
|
nim r tools/ci_testresults.nim
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
triggers:
|
||||||
|
- action: email
|
||||||
|
condition: failure
|
||||||
|
to: Andreas Rumpf <rumpf_a@web.de>
|
||||||
37
.builds/openbsd_0.yml
Normal file
37
.builds/openbsd_0.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||||
|
|
||||||
|
image: openbsd/latest
|
||||||
|
packages:
|
||||||
|
- gmake
|
||||||
|
- sqlite3
|
||||||
|
- node
|
||||||
|
- boehm-gc
|
||||||
|
- pcre
|
||||||
|
- sfml
|
||||||
|
- sdl2
|
||||||
|
- libffi
|
||||||
|
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- https://github.com/nim-lang/Nim
|
||||||
|
environment:
|
||||||
|
NIM_TESTAMENT_BATCH: "0_2"
|
||||||
|
CC: /usr/bin/clang
|
||||||
|
tasks:
|
||||||
|
- setup: |
|
||||||
|
set -e
|
||||||
|
cd Nim
|
||||||
|
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||||
|
$nim_csources c --skipUserCfg --skipParentCfg koch
|
||||||
|
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||||
|
- test: |
|
||||||
|
set -e
|
||||||
|
cd Nim
|
||||||
|
if ! ./koch runCI; then
|
||||||
|
nim r tools/ci_testresults.nim
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
triggers:
|
||||||
|
- action: email
|
||||||
|
condition: failure
|
||||||
|
to: Andreas Rumpf <rumpf_a@web.de>
|
||||||
37
.builds/openbsd_1.yml
Normal file
37
.builds/openbsd_1.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||||
|
|
||||||
|
image: openbsd/latest
|
||||||
|
packages:
|
||||||
|
- gmake
|
||||||
|
- sqlite3
|
||||||
|
- node
|
||||||
|
- boehm-gc
|
||||||
|
- pcre
|
||||||
|
- sfml
|
||||||
|
- sdl2
|
||||||
|
- libffi
|
||||||
|
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- https://github.com/nim-lang/Nim
|
||||||
|
environment:
|
||||||
|
NIM_TESTAMENT_BATCH: "1_2"
|
||||||
|
CC: /usr/bin/clang
|
||||||
|
tasks:
|
||||||
|
- setup: |
|
||||||
|
set -e
|
||||||
|
cd Nim
|
||||||
|
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||||
|
$nim_csources c --skipUserCfg --skipParentCfg koch
|
||||||
|
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||||
|
- test: |
|
||||||
|
set -e
|
||||||
|
cd Nim
|
||||||
|
if ! ./koch runCI; then
|
||||||
|
nim r tools/ci_testresults.nim
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
triggers:
|
||||||
|
- action: email
|
||||||
|
condition: failure
|
||||||
|
to: Andreas Rumpf <rumpf_a@web.de>
|
||||||
3
.gitattributes
vendored
3
.gitattributes
vendored
|
|
@ -4,6 +4,3 @@
|
||||||
# duplicated, which is easily identifiable and fixable.
|
# duplicated, which is easily identifiable and fixable.
|
||||||
/changelog.md merge=union
|
/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
|
|
||||||
|
|
|
||||||
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Have you found an unexpected behavior? Use this template.
|
||||||
|
title: Think about the title, twice
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Function `echo` outputs the wrong string.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```nim
|
||||||
|
echo "Hello World!"
|
||||||
|
# This code should be a minimum reproducible example:
|
||||||
|
# try to simplify and minimize as much as possible. If it's a compiler
|
||||||
|
# issue, try to minimize further by removing any imports if possible.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Current Output
|
||||||
|
please check whether the problem still exists in git head before posting,
|
||||||
|
see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
||||||
|
```
|
||||||
|
Hola mundo!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Expected Output
|
||||||
|
```
|
||||||
|
Hello World!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Possible Solution
|
||||||
|
|
||||||
|
* In file xyz there is a call that might be the cause of it.
|
||||||
|
|
||||||
|
### Additional Information
|
||||||
|
If it's a regression, you can help us by identifying which version introduced
|
||||||
|
the bug, see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
|
||||||
|
or at least try known past releases (eg `choosenim 1.2.0`).
|
||||||
|
|
||||||
|
If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
|
||||||
|
which should give more context on a compiler crash.
|
||||||
|
|
||||||
|
* Issue #abc is related, but different because of ...
|
||||||
|
* This issue is blocking my project xyz
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nim -v
|
||||||
|
Nim Compiler Version 0.1.2
|
||||||
|
# make sure to include the git hash if not using a tagged release
|
||||||
|
```
|
||||||
75
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
75
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -1,75 +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!** :bug:
|
|
||||||
Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
Use DETAILED DESCRIPTIVE information about the problem.
|
|
||||||
Here, you go into more details about your Bug report. This section can be a few paragraphs long.
|
|
||||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: nim-version
|
|
||||||
attributes:
|
|
||||||
label: Nim Version
|
|
||||||
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- 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 and 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 and detailed information will be fixed faster.
|
|
||||||
render: text
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: possible-solution
|
|
||||||
attributes:
|
|
||||||
label: Possible Solution
|
|
||||||
description: Propose a possible solution.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: extra-info
|
|
||||||
attributes:
|
|
||||||
label: Additional Information
|
|
||||||
description: Any additional relevant information.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
- Thanks for your contributions!, your Bug report will receive feedback from the community soon...
|
|
||||||
- Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
|
||||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
|
||||||
- If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
|
|
||||||
which should give more context on a compiler crash.
|
|
||||||
- If it's a regression, you can help us by identifying which version introduced the bug,
|
|
||||||
see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
|
|
||||||
or at least try known past releases (eg `choosenim 1.2.0`).
|
|
||||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
|
||||||
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Do you want to suggest a new feature? Use this template.
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!---
|
||||||
|
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)
|
|
||||||
22
.github/workflows/ci_docs.yml
vendored
22
.github/workflows/ci_docs.yml
vendored
|
|
@ -6,7 +6,6 @@ on:
|
||||||
- 'compiler/renderverbatim.nim'
|
- 'compiler/renderverbatim.nim'
|
||||||
- 'config/nimdoc.cfg'
|
- 'config/nimdoc.cfg'
|
||||||
- 'doc/**.rst'
|
- 'doc/**.rst'
|
||||||
- 'doc/**.md'
|
|
||||||
- 'doc/nimdoc.css'
|
- 'doc/nimdoc.css'
|
||||||
- 'lib/**.nim'
|
- 'lib/**.nim'
|
||||||
- 'nimdoc/testproject/expected/testproject.html'
|
- 'nimdoc/testproject/expected/testproject.html'
|
||||||
|
|
@ -14,7 +13,6 @@ on:
|
||||||
- 'tools/kochdocs.nim'
|
- 'tools/kochdocs.nim'
|
||||||
- '.github/workflows/ci_docs.yml'
|
- '.github/workflows/ci_docs.yml'
|
||||||
- 'koch.nim'
|
- 'koch.nim'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
# Run only on changes on these files.
|
# Run only on changes on these files.
|
||||||
paths:
|
paths:
|
||||||
|
|
@ -22,7 +20,6 @@ on:
|
||||||
- 'compiler/renderverbatim.nim'
|
- 'compiler/renderverbatim.nim'
|
||||||
- 'config/nimdoc.cfg'
|
- 'config/nimdoc.cfg'
|
||||||
- 'doc/**.rst'
|
- 'doc/**.rst'
|
||||||
- 'doc/**.md'
|
|
||||||
- 'doc/nimdoc.css'
|
- 'doc/nimdoc.css'
|
||||||
- 'lib/**.nim'
|
- 'lib/**.nim'
|
||||||
- 'nimdoc/testproject/expected/testproject.html'
|
- 'nimdoc/testproject/expected/testproject.html'
|
||||||
|
|
@ -31,10 +28,6 @@ on:
|
||||||
- '.github/workflows/ci_docs.yml'
|
- '.github/workflows/ci_docs.yml'
|
||||||
- 'koch.nim'
|
- 'koch.nim'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
|
|
@ -47,15 +40,14 @@ jobs:
|
||||||
- target: windows
|
- target: windows
|
||||||
os: windows-2019
|
os: windows-2019
|
||||||
- target: osx
|
- target: osx
|
||||||
os: macos-11
|
os: macos-10.15
|
||||||
|
|
||||||
name: ${{ matrix.target }}
|
name: ${{ matrix.target }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 60 # refs bug #18178
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 'Checkout'
|
- name: 'Checkout'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
|
|
@ -67,9 +59,11 @@ jobs:
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -e
|
mkdir dist
|
||||||
. ci/funs.sh
|
curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
|
||||||
nimInternalInstallDepsWindows
|
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||||
|
7z x dist/mingw64.7z -odist
|
||||||
|
7z x dist/dlls.zip -obin
|
||||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||||
|
|
||||||
- name: 'Add build binaries to PATH'
|
- name: 'Add build binaries to PATH'
|
||||||
|
|
@ -111,7 +105,7 @@ jobs:
|
||||||
if: |
|
if: |
|
||||||
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
||||||
matrix.target == 'linux'
|
matrix.target == 'linux'
|
||||||
uses: crazy-max/ghaction-github-pages@v3
|
uses: crazy-max/ghaction-github-pages@v1
|
||||||
with:
|
with:
|
||||||
build_dir: doc/html
|
build_dir: doc/html
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
42
.github/workflows/ci_packages.yml
vendored
42
.github/workflows/ci_packages.yml
vendored
|
|
@ -1,39 +1,27 @@
|
||||||
name: Packages CI
|
name: Packages CI
|
||||||
on:
|
on: [push, pull_request]
|
||||||
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:
|
jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, macos-11]
|
os: [ubuntu-20.04, macos-10.15]
|
||||||
cpu: [amd64]
|
cpu: [amd64]
|
||||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 60 # refs bug #18178
|
|
||||||
env:
|
env:
|
||||||
NIM_TEST_PACKAGES: "1"
|
NIM_TEST_PACKAGES: "1"
|
||||||
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
|
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
|
||||||
steps:
|
steps:
|
||||||
- name: 'Checkout'
|
- name: 'Checkout'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: 'Install node.js 16.x'
|
- name: 'Install node.js 16.x'
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '16.x'
|
||||||
|
|
||||||
|
|
@ -52,10 +40,12 @@ jobs:
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -e
|
mkdir dist
|
||||||
. ci/funs.sh
|
curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
|
||||||
nimInternalInstallDepsWindows
|
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
7z x dist/mingw64.7z -odist
|
||||||
|
7z x dist/dlls.zip -obin
|
||||||
|
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||||
|
|
||||||
- name: 'Add build binaries to PATH'
|
- name: 'Add build binaries to PATH'
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
@ -69,6 +59,14 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||||
|
|
||||||
- name: 'koch, Run CI'
|
- name: 'Build koch'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
run: nim c koch
|
||||||
|
- name: 'Run CI'
|
||||||
|
shell: bash
|
||||||
|
run: ./koch runCI
|
||||||
|
|
||||||
|
- name: 'Show failed tests'
|
||||||
|
if: failure()
|
||||||
|
shell: bash
|
||||||
|
run: nim c -r tools/ci_testresults.nim
|
||||||
|
|
|
||||||
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@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: 'Install node.js 16.x'
|
|
||||||
uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: '16.x'
|
|
||||||
|
|
||||||
- name: 'Install dependencies (Linux amd64)'
|
|
||||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
|
||||||
run: |
|
|
||||||
sudo apt-fast update -qq
|
|
||||||
DEBIAN_FRONTEND='noninteractive' \
|
|
||||||
sudo apt-fast install --no-install-recommends -yq \
|
|
||||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
|
||||||
valgrind libc6-dbg libblas-dev xorg-dev
|
|
||||||
- name: 'Install dependencies (macOS)'
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: brew install boehmgc make sfml gtk+3
|
|
||||||
- name: 'Install dependencies (Windows)'
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
nimInternalInstallDepsWindows
|
|
||||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'Add build binaries to PATH'
|
|
||||||
shell: bash
|
|
||||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'System information'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimCiSystemInfo
|
|
||||||
|
|
||||||
- name: 'Build csourcesAny'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
|
||||||
|
|
||||||
- name: '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@v6
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -3,9 +3,9 @@
|
||||||
!*.*
|
!*.*
|
||||||
|
|
||||||
# Cache
|
# Cache
|
||||||
nimcache*/
|
nimcache/
|
||||||
rnimcache*/
|
rnimcache/
|
||||||
dnimcache*/
|
dnimcache/
|
||||||
|
|
||||||
*.o
|
*.o
|
||||||
!/icons/*.o
|
!/icons/*.o
|
||||||
|
|
@ -67,7 +67,6 @@ testament.db
|
||||||
|
|
||||||
/csources
|
/csources
|
||||||
/csources_v1
|
/csources_v1
|
||||||
/csources_v2
|
|
||||||
|
|
||||||
/dist/
|
/dist/
|
||||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
# xxx unused, out of date
|
image: ubuntu:16.04
|
||||||
|
|
||||||
image: ubuntu:18.04
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- pre-build
|
- pre-build
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
trigger:
|
trigger:
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- 'devel'
|
- '*'
|
||||||
- 'version-*'
|
|
||||||
pr:
|
pr:
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
|
|
@ -20,19 +19,19 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
Linux_amd64:
|
Linux_amd64:
|
||||||
vmImage: 'ubuntu-20.04'
|
vmImage: 'ubuntu-16.04'
|
||||||
CPU: amd64
|
CPU: amd64
|
||||||
# regularly breaks, refs bug #17325
|
# pending bug #17325 (broken again)
|
||||||
Linux_i386:
|
# Linux_i386:
|
||||||
# on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
|
# # bug #17325: fails on 'ubuntu-16.04' because it now errors with:
|
||||||
# g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||||
vmImage: 'ubuntu-18.04'
|
# vmImage: 'ubuntu-18.04'
|
||||||
CPU: i386
|
# CPU: i386
|
||||||
OSX_amd64:
|
OSX_amd64:
|
||||||
vmImage: 'macOS-11'
|
vmImage: 'macOS-10.15'
|
||||||
CPU: amd64
|
CPU: amd64
|
||||||
OSX_amd64_cpp:
|
OSX_amd64_cpp:
|
||||||
vmImage: 'macOS-11'
|
vmImage: 'macOS-10.15'
|
||||||
CPU: amd64
|
CPU: amd64
|
||||||
NIM_COMPILE_TO_CPP: true
|
NIM_COMPILE_TO_CPP: true
|
||||||
Windows_amd64_batch0_3:
|
Windows_amd64_batch0_3:
|
||||||
|
|
@ -134,7 +133,11 @@ jobs:
|
||||||
- bash: |
|
- bash: |
|
||||||
set -e
|
set -e
|
||||||
. ci/funs.sh
|
. ci/funs.sh
|
||||||
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
|
||||||
echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
||||||
|
|
||||||
displayName: 'Install dependencies (Windows)'
|
displayName: 'Install dependencies (Windows)'
|
||||||
|
|
@ -160,9 +163,13 @@ jobs:
|
||||||
# condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
# condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||||
# displayName: 'Restore built csourcesAny'
|
# displayName: 'Restore built csourcesAny'
|
||||||
|
|
||||||
- bash: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
- bash: nim c koch
|
||||||
# would could also show on failure: echo '##vso[task.complete result=Failed]'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||||
displayName: 'koch, Run CI'
|
displayName: 'Build koch'
|
||||||
|
|
||||||
|
# set result to omit the "bash exited with error code '1'" message
|
||||||
|
- bash: ./koch runCI || echo '##vso[task.complete result=Failed]'
|
||||||
|
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
||||||
|
displayName: 'Run CI'
|
||||||
env:
|
env:
|
||||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ which nim > /dev/null || (echo "nim not in PATH"; exit 1)
|
||||||
which gdb > /dev/null || (echo "gdb 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)
|
which readlink > /dev/null || (echo "readlink not in PATH."; exit 1)
|
||||||
|
|
||||||
if [[ $(uname -s) == Darwin || $(uname -s) == *BSD ]]; then
|
if [[ "$(uname -s)" == "Darwin" || "(uname -s)" == *"BSD" ]]; then
|
||||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim))))
|
NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim))))
|
||||||
else
|
else
|
||||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
|
NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Find out where the pretty printer Python module is
|
# Find out where the pretty printer Python module is
|
||||||
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py"
|
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py"
|
||||||
|
|
||||||
# Run GDB with the additional arguments that load the pretty printers
|
# Run GDB with the additional arguments that load the pretty printers
|
||||||
# Set the environment variable `NIM_GDB` to overwrite the call to a
|
# Set the environment variable `NIM_GDB` to overwrite the call to a
|
||||||
|
|
|
||||||
1
bin/nim-gdb.bash
Symbolic link
1
bin/nim-gdb.bash
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
nim-gdb
|
||||||
|
|
@ -3,7 +3,7 @@ for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i)
|
||||||
|
|
||||||
for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi)
|
for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi)
|
||||||
|
|
||||||
set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py
|
set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\nim-gdb.py
|
||||||
set @NIM_GDB=gdb.exe
|
set @NIM_GDB=gdb.exe
|
||||||
|
|
||||||
@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
|
@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
|
||||||
echo "building from csources: %nim_csources%"
|
echo "building from csources: %nim_csources%"
|
||||||
|
|
||||||
if not exist %nim_csourcesDir% (
|
if not exist %nim_csourcesDir% (
|
||||||
git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
|
git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir%
|
||||||
)
|
)
|
||||||
|
|
||||||
if not exist %nim_csources% (
|
if not exist %nim_csources% (
|
||||||
|
|
@ -24,6 +24,6 @@ if not exist %nim_csources% (
|
||||||
cd ..
|
cd ..
|
||||||
copy /y bin\nim.exe %nim_csources%
|
copy /y bin\nim.exe %nim_csources%
|
||||||
)
|
)
|
||||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
bin\nim.exe c --skipUserCfg --skipParentCfg --hints:off koch
|
||||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#! /bin/sh
|
||||||
# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||||
|
|
||||||
# build development version of the compiler; can be rerun safely.
|
# build development version of the compiler; can be rerun safely.
|
||||||
|
|
@ -11,7 +11,7 @@ set -e # exit on first error
|
||||||
. ci/funs.sh
|
. ci/funs.sh
|
||||||
nimBuildCsourcesIfNeeded "$@"
|
nimBuildCsourcesIfNeeded "$@"
|
||||||
|
|
||||||
echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
echo_run bin/nim c --skipUserCfg --skipParentCfg --hints:off koch
|
||||||
echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||||
echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
|
echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||||
|
|
||||||
|
|
|
||||||
409
changelog.md
409
changelog.md
|
|
@ -1,31 +1,428 @@
|
||||||
# v2.2.0 - yyyy-mm-dd
|
# v1.6.x - yyyy-mm-dd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Changes affecting backward compatibility
|
## Changes affecting backward compatibility
|
||||||
|
|
||||||
|
- `cuchar` now aliases `uint8` instead of `char`
|
||||||
|
|
||||||
|
- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent,
|
||||||
|
see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior.
|
||||||
|
|
||||||
|
- An enum now can't be converted to another enum directly, you must use `ord` (or `cast`, but
|
||||||
|
compiler won't help if you misuse it).
|
||||||
|
```
|
||||||
|
type A = enum a1, a2
|
||||||
|
type B = enum b1, b2
|
||||||
|
doAssert not compiles(a1.B)
|
||||||
|
doAssert compiles(a1.ord.B)
|
||||||
|
```
|
||||||
|
for a transition period, use `-d:nimLegacyConvEnumEnum`.
|
||||||
|
|
||||||
|
- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior
|
||||||
|
silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior.
|
||||||
|
|
||||||
|
- `math.round` now is rounded "away from zero" in JS backend which is consistent
|
||||||
|
with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior.
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- Custom pragma values have now an API for use in macros.
|
||||||
|
|
||||||
|
- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`,
|
||||||
|
unless `-d:nimLegacyHomeDir` is specified (for a transition period).
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
- `hashes.hash` now supports `object` and `ref` (can be overloaded in user code).
|
||||||
|
For a transition period, use `-d:nimLegacyNoHashRef` to avoid defining `hash(ref)`.
|
||||||
|
- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`,
|
||||||
|
`hashes.hash(closure)` has also been improved.
|
||||||
|
|
||||||
|
- The unary slice `..b` was removed, use `0..b` instead or use `-d:nimLegacyUnarySlice`
|
||||||
|
for a deprecation period.
|
||||||
|
|
||||||
|
- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`.
|
||||||
|
|
||||||
|
- Nim compiler now adds ASCII unit separator `\31` before a newline for every generated
|
||||||
|
message (potentially multiline), so tooling can tell when messages start and end.
|
||||||
|
|
||||||
|
- `random.initRand(seed)` now produces non-skewed values for the 1st call to `rand()` after
|
||||||
|
initialization with a small (< 30000) seed. Use `-d:nimLegacyRandomInitRand` to restore
|
||||||
|
previous behavior for a transition time, see PR #17467.
|
||||||
|
|
||||||
|
- `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings.
|
||||||
|
Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period.
|
||||||
|
|
||||||
|
- `json` and `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.
|
||||||
|
|
||||||
## Standard library additions and changes
|
## Standard library additions and changes
|
||||||
|
- Added support for parenthesized expressions in `strformat`
|
||||||
|
|
||||||
[//]: # "Changes:"
|
- Fixed buffer overflow bugs in `net`
|
||||||
|
|
||||||
|
- Exported `sslHandle` from `net` and `asyncnet`.
|
||||||
|
|
||||||
|
- Added `sections` iterator in `parsecfg`.
|
||||||
|
|
||||||
|
- Make custom op in macros.quote work for all statements.
|
||||||
|
|
||||||
|
- On Windows the SSL library now checks for valid certificates.
|
||||||
|
It uses the `cacert.pem` file for this purpose 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.
|
||||||
|
|
||||||
|
|
||||||
[//]: # "Additions:"
|
- Make `{.requiresInit.}` pragma to work for `distinct` types.
|
||||||
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
|
|
||||||
|
|
||||||
[//]: # "Deprecations:"
|
- Added a macros `enumLen` for returning the number of items in an enum to the
|
||||||
|
`typetraits.nim` module.
|
||||||
|
|
||||||
|
- `prelude` now works with the JavaScript target.
|
||||||
|
Added `sequtils` import to `prelude`.
|
||||||
|
`prelude` can now be used via `include std/prelude`, but `include prelude` still works.
|
||||||
|
|
||||||
|
- Added `almostEqual` in `math` for comparing two float values using a machine epsilon.
|
||||||
|
|
||||||
|
- Added `clamp` in `math` which allows using a `Slice` to clamp to a value.
|
||||||
|
|
||||||
|
- The JSON module 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.formJson`,`jsonutils.toJson` now work with `uint|uint64`
|
||||||
|
instead of raising (as in 1.4) or giving wrong results (as in 1.2).
|
||||||
|
|
||||||
|
- added `jsonutils.jsonTo` overload with `opt = Joptions()` param.
|
||||||
|
|
||||||
|
- `jsonutils` now handles `cstring` (including as Table key), and `set`.
|
||||||
|
|
||||||
|
- Added an overload for the `collect` macro that inferes the container type based
|
||||||
|
on the syntax of the last expression. Works with std seqs, tables and sets.
|
||||||
|
|
||||||
|
- Added `randState` template that exposes the default random number generator.
|
||||||
|
Useful for library authors.
|
||||||
|
|
||||||
|
- Added `random.initRand()` overload with no argument which uses the current time as a seed.
|
||||||
|
|
||||||
|
- `random.initRand(seed)` now allows `seed == 0`.
|
||||||
|
|
||||||
|
- Added `std/sysrand` module to get random numbers from a secure source
|
||||||
|
provided by the operating system.
|
||||||
|
|
||||||
|
- Added `std/enumutils` module. 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 `typetraits.HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes.
|
||||||
|
|
||||||
|
- Removed deprecated `iup` module from stdlib, it has already moved to
|
||||||
|
[nimble](https://github.com/nim-lang/iup).
|
||||||
|
|
||||||
|
- various functions in `httpclient` now accept `url` of type `Uri`. Moreover `request` function's
|
||||||
|
`httpMethod` argument of type `string` was deprecated in favor of `HttpMethod` enum type.
|
||||||
|
|
||||||
|
- `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`.
|
||||||
|
|
||||||
|
- Added `cmpMem` to `system`.
|
||||||
|
|
||||||
|
- `doAssertRaises` now correctly handles foreign exceptions.
|
||||||
|
|
||||||
|
- Added `asyncdispatch.activeDescriptors` that returns the number of currently
|
||||||
|
active async event handles/file descriptors.
|
||||||
|
|
||||||
|
- Added `getPort` to `asynchttpserver`.
|
||||||
|
|
||||||
|
- `--gc:orc` is now 10% faster than previously for common workloads. If
|
||||||
|
you have trouble with its changed behavior, compile with `-d:nimOldOrc`.
|
||||||
|
|
||||||
|
|
||||||
[//]: # "Removals:"
|
- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`,
|
||||||
|
determining preferred I/O block size for this file object.
|
||||||
|
|
||||||
|
- Added a simpler to use `io.readChars` overload.
|
||||||
|
|
||||||
|
- Added `**` to jsffi.
|
||||||
|
|
||||||
|
- `writeStackTrace` is available in JS backend now.
|
||||||
|
|
||||||
|
- Added `decodeQuery` to `std/uri`.
|
||||||
|
|
||||||
|
- `strscans.scanf` now supports parsing single characters.
|
||||||
|
|
||||||
|
- `strscans.scanTuple` added which uses `strscans.scanf` internally,
|
||||||
|
returning a tuple which can be unpacked for easier usage of `scanf`.
|
||||||
|
|
||||||
|
- 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.[]=`.
|
||||||
|
|
||||||
|
- Added `math.isNaN`.
|
||||||
|
|
||||||
|
- Added `jsbigints` module, arbitrary precision integers for JavaScript target.
|
||||||
|
|
||||||
|
- Added `math.copySign`.
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
- Added `euclDiv` and `euclMod` to `math`.
|
||||||
|
|
||||||
|
- Added `httpcore.is1xx` and missing HTTP codes.
|
||||||
|
|
||||||
|
- Added `jsconsole.jsAssert` for JavaScript target.
|
||||||
|
|
||||||
|
- Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs.
|
||||||
|
https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||||
|
|
||||||
|
- Added `socketstream` module that wraps sockets in the stream interface
|
||||||
|
|
||||||
|
- Added `sugar.dumpToString` which improves on `sugar.dump`.
|
||||||
|
|
||||||
|
- Added `math.signbit`.
|
||||||
|
|
||||||
|
- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably)
|
||||||
|
|
||||||
|
- In `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.
|
||||||
|
|
||||||
|
- Deprecated `any`. See https://github.com/nim-lang/RFCs/issues/281
|
||||||
|
|
||||||
|
- 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 `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 experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
|
||||||
|
|
||||||
|
- Added `compilesettings.SingleValueSetting.libPath`.
|
||||||
|
|
||||||
|
- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding
|
||||||
|
issues like https://github.com/nim-lang/Nim/issues/13063 (which affected error messages)
|
||||||
|
for modules importing `std/wrapnils`.
|
||||||
|
Added `??.` macro which returns an `Option`.
|
||||||
|
|
||||||
|
- Added `math.frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead.
|
||||||
|
|
||||||
|
- `parseopt.initOptParser` has been made available and `parseopt` has been
|
||||||
|
added back to `prelude` for all backends. Previously `initOptParser` was
|
||||||
|
unavailable if the `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 `prelude` for JS, as it could not be imported.
|
||||||
|
|
||||||
|
- Added `system.prepareStrMutation` for better support of low
|
||||||
|
level `moveMem`, `copyMem` operations for Orc's copy-on-write string
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
- Added `std/strbasics` for high performance string operations.
|
||||||
|
Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`.
|
||||||
|
|
||||||
|
|
||||||
|
- Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`.
|
||||||
|
|
||||||
|
- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
|
||||||
|
and `$none(int)` to `"none(int)"` instead of `"None[int]"`.
|
||||||
|
|
||||||
|
- Added `algorithm.merge`.
|
||||||
|
|
||||||
|
|
||||||
|
- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) wrapper for JavaScript target.
|
||||||
|
|
||||||
|
- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) wrapper for JavaScript target.
|
||||||
|
|
||||||
|
- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) wrapper for JavaScript target.
|
||||||
|
|
||||||
|
- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible
|
||||||
|
with most other languages.
|
||||||
|
|
||||||
|
- Removed support for named procs in `sugar.=>`.
|
||||||
|
|
||||||
|
- 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 `std/channels`.
|
||||||
|
|
||||||
|
- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals).
|
||||||
|
|
||||||
|
- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time
|
||||||
|
zone offsets without colons, e.g. `UTC+7 -> +0700`.
|
||||||
|
|
||||||
|
- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
|
||||||
|
|
||||||
|
- Added dollar `$` and `len` for `jsre.RegExp`.
|
||||||
|
|
||||||
|
- Added `std/tasks`.
|
||||||
|
|
||||||
|
- Added `hasDataBuffered` to `asyncnet`.
|
||||||
|
|
||||||
|
- Added `hasClosure` to `std/typetraits`.
|
||||||
|
|
||||||
|
- Added `std/tempfiles`.
|
||||||
|
|
||||||
|
- Added `genasts.genAst` that avoids the problems inherent with `quote do` and can
|
||||||
|
be used as a replacement.
|
||||||
|
|
||||||
|
- Added `copyWithin` [for `seq` and `array` for JavaScript targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin).
|
||||||
|
|
||||||
|
|
||||||
## Language changes
|
## Language changes
|
||||||
|
|
||||||
|
- `nimscript` now handles `except Exception as e`.
|
||||||
|
|
||||||
|
- The `cstring` doesn't support `[]=` operator in JS backend.
|
||||||
|
|
||||||
|
- nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time.
|
||||||
|
|
||||||
|
- `typetraits.distinctBase` now is identity instead of error for non distinct types.
|
||||||
|
|
||||||
|
- `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`;
|
||||||
|
use `-d:nimLegacyCopyFile` for OSX < 10.5.
|
||||||
|
|
||||||
|
- The required name of case statement macros for the experimental
|
||||||
|
`caseStmtMacros` feature has changed from `match` to `` `case` ``.
|
||||||
|
|
||||||
|
- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages.
|
||||||
|
|
||||||
|
- The unary minus in `-1` is now part of the integer literal, it is now parsed as a single token.
|
||||||
|
This implies that edge cases like `-128'i8` finally work correctly.
|
||||||
|
|
||||||
|
- Custom numeric literals (e.g. `-128'bignum`) are now supported.
|
||||||
|
|
||||||
|
- Tuple expressions are now parsed consistently as
|
||||||
|
`nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`.
|
||||||
|
|
||||||
|
- `nim e` now accepts arbitrary file extensions for the nimscript file,
|
||||||
|
although `.nims` is still the preferred extension in general.
|
||||||
|
|
||||||
|
- Added `iterable[T]` type class to match called iterators, which enables writing:
|
||||||
|
`template fn(a: iterable)` instead of `template fn(a: untyped)`
|
||||||
|
|
||||||
|
- A new import syntax `import foo {.all.}` now allows to import all symbols (public or private)
|
||||||
|
from `foo`. It works in combination with all pre-existing import features.
|
||||||
|
This reduces or eliminates the need for workarounds such as using `include` (which has known issues)
|
||||||
|
when you need a private symbol for testing or making some internal APIs public just because
|
||||||
|
another internal module needs those.
|
||||||
|
It also helps mitigate the lack of cyclic imports in some cases.
|
||||||
|
|
||||||
|
- Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields
|
||||||
|
for an object type in the current scope.
|
||||||
|
|
||||||
|
- `typeof(voidStmt)` now works and returns `void`.
|
||||||
|
|
||||||
## Compiler changes
|
## Compiler changes
|
||||||
|
|
||||||
|
- Added `--declaredlocs` to show symbol declaration location in messages.
|
||||||
|
|
||||||
|
- Deprecated `TaintedString` and `--taintmode`.
|
||||||
|
|
||||||
|
- Deprecated `--nilseqs` which is now a noop.
|
||||||
|
|
||||||
|
- Added `--spellSuggest` to show spelling suggestions on typos.
|
||||||
|
|
||||||
|
- Added `--filenames:abs|canonical|legacyRelProj` which replaces --listFullPaths:on|off
|
||||||
|
|
||||||
|
- Added `--processing:dots|filenames|off` which customizes `hintProcessing`
|
||||||
|
|
||||||
|
- Source+Edit links now appear on top of every docgen'd page when
|
||||||
|
`nim doc --git.url:url ...` is given.
|
||||||
|
|
||||||
|
- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`.
|
||||||
|
|
||||||
|
- VM now supports `addr(mystring[ind])` (index + index assignment)
|
||||||
|
|
||||||
|
- Added `--hintAsError` with similar semantics as `--warningAsError`.
|
||||||
|
|
||||||
|
- TLS: OSX 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.
|
||||||
|
|
||||||
|
- Now array literals(JS backend) uses JS typed arrays when the corresponding js typed array exists, for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`.
|
||||||
|
|
||||||
|
- docgen: rst files can now use single backticks instead of double backticks and correctly render
|
||||||
|
in both rst2html (as before) as well as common tools rendering rst directly (e.g. github), by
|
||||||
|
adding: `default-role:: code` directive inside the rst file, which is now handled by rst2html.
|
||||||
|
|
||||||
|
- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have certain hints/warnings
|
||||||
|
|
||||||
|
- Added `then`, `catch` to `asyncjs`, for now hidden behind `-d:nimExperimentalAsyncjsThen`.
|
||||||
|
|
||||||
|
- `--newruntime` and `--refchecks` are deprecated.
|
||||||
|
|
||||||
|
- Added `unsafeIsolate` and `extract` to `std/isolation`.
|
||||||
|
|
||||||
|
- `--hint:CC` now goes to stderr (like all other hints) instead of stdout.
|
||||||
|
|
||||||
|
- 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:
|
||||||
|
nim r main # recompiles
|
||||||
|
nim r -d:foo main # recompiles
|
||||||
|
nim r main # uses cached binary
|
||||||
|
nim r main arg1 arg2 # ditto (runtime arguments are irrelevant)
|
||||||
|
|
||||||
|
- 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`.
|
||||||
|
|
||||||
## Tool changes
|
## Tool changes
|
||||||
|
|
||||||
|
- 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 |`.
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
|
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
|
||||||
inheritance to prevent this issue.
|
inheritance to prevent this issue.
|
||||||
- The ``not nil`` type annotation now has to be enabled explicitly
|
- 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.
|
feature works with Nim's containers.
|
||||||
- The parser now warns about inconsistent spacing around binary operators as
|
- The parser now warns about inconsistent spacing around binary operators as
|
||||||
these can easily be confused with unary operators. This warning will likely
|
these can easily be confused with unary operators. This warning will likely
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@
|
||||||
- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
|
- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
|
||||||
- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
|
- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
|
||||||
|
|
||||||
- Removed `sharedlist.initSharedList`, was deprecated and produces undefined behaviour.
|
- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour.
|
||||||
|
|
||||||
- There is a new experimental feature called "strictFuncs" which makes the definition of
|
- There is a new experimental feature called "strictFuncs" which makes the definition of
|
||||||
`.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs)
|
`.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs)
|
||||||
|
|
|
||||||
|
|
@ -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,367 +0,0 @@
|
||||||
# v2.0.0 - yyyy-mm-dd
|
|
||||||
|
|
||||||
|
|
||||||
## Changes affecting backward compatibility
|
|
||||||
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `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 `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.
|
|
||||||
|
|
||||||
- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore.
|
|
||||||
|
|
||||||
- The `gc:v2` option is removed.
|
|
||||||
|
|
||||||
- The `mainmodule` and `m` options are removed.
|
|
||||||
|
|
||||||
- The `threads:on` option is now the default.
|
|
||||||
|
|
||||||
- Optional parameters in combination with `: body` syntax (RFC #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 fullfill 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`.
|
|
||||||
|
|
||||||
- `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.
|
|
||||||
|
|
||||||
- Removed the `nimIncrSeqV3` define.
|
|
||||||
|
|
||||||
- `macros.getImpl` for `const` symbols now returns the full definition node
|
|
||||||
(as `nnkConstDef`) rather than the AST of the constant value.
|
|
||||||
|
|
||||||
- Lock levels are deprecated, now a noop.
|
|
||||||
|
|
||||||
- ORC is now the default memory management strategy. Use
|
|
||||||
`--mm:refc` for a transition period.
|
|
||||||
|
|
||||||
- `strictEffects` are no longer experimental.
|
|
||||||
Use `legacy:laxEffects` to keep backward compatibility.
|
|
||||||
|
|
||||||
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
|
|
||||||
if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`.
|
|
||||||
|
|
||||||
- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning.
|
|
||||||
This warning will become an error in future versions! Use a `cast` operation
|
|
||||||
like `cast[cstring](x)` instead.
|
|
||||||
|
|
||||||
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
|
|
||||||
|
|
||||||
- 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 are moved to nimble packages, use `nimble` to install them:
|
|
||||||
- `std/punycode` => `punycode`
|
|
||||||
- `std/asyncftpclient` => `asyncftpclient`
|
|
||||||
- `std/smtp` => `smtp`
|
|
||||||
- `std/db_common` => `db_connector/db_common`
|
|
||||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
|
||||||
- `std/db_mysql` => `db_connector/db_mysql`
|
|
||||||
- `std/db_postgres` => `db_connector/db_postgres`
|
|
||||||
- `std/db_odbc` => `db_connector/db_odbc`
|
|
||||||
|
|
||||||
- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
|
|
||||||
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
|
||||||
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
|
||||||
|
|
||||||
- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered.
|
|
||||||
|
|
||||||
- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
|
||||||
|
|
||||||
- Underscores (`_`) as routine parameters are now ignored and cannot be used in the routine body.
|
|
||||||
The following code now does not compile:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc foo(_: int): int = _ + 1
|
|
||||||
echo foo(1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, the following code now compiles:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc foo(_, _: int): int = 123
|
|
||||||
echo foo(1, 2)
|
|
||||||
```
|
|
||||||
- Underscores (`_`) as generic parameters are not supported and cannot be used.
|
|
||||||
Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile
|
|
||||||
proc foo[T](t: typedesc[T]): string = "BAR" # Can compile
|
|
||||||
```
|
|
||||||
|
|
||||||
- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
- Module `colors` expanded with missing colors from the CSS color standard.
|
|
||||||
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
|
|
||||||
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
|
|
||||||
- The `md5` module now works at compile time and in JavaScript.
|
|
||||||
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables.
|
|
||||||
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
|
|
||||||
making limiting it to just the first char (`last = 0`) valid.
|
|
||||||
- `random.rand` now works with `Ordinal`s.
|
|
||||||
- Undeprecated `os.isvalidfilename`.
|
|
||||||
- `std/oids` now uses `int64` to store time internally (before it was int32).
|
|
||||||
- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`.
|
|
||||||
- `std/uri.Uri.isIpv6` is now exported.
|
|
||||||
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
|
|
||||||
|
|
||||||
|
|
||||||
- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`.
|
|
||||||
- `std/jsfetch.newFetchOptions` now has default values for all parameters
|
|
||||||
- `std/jsformdata` now accepts `Blob` data type.
|
|
||||||
|
|
||||||
- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433).
|
|
||||||
|
|
||||||
- New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall.
|
|
||||||
|
|
||||||
This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
|
|
||||||
|
|
||||||
On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
|
|
||||||
|
|
||||||
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
|
|
||||||
```sh
|
|
||||||
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
|
|
||||||
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
|
|
||||||
$ sh build.sh # per unix install instructions
|
|
||||||
$ bin/nim c koch # per unix install instructions
|
|
||||||
$ ./koch boot -d:release # per unix install instructions
|
|
||||||
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
|
|
||||||
```
|
|
||||||
|
|
||||||
This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular).
|
|
||||||
|
|
||||||
[//]: # "Additions:"
|
|
||||||
- Added ISO 8601 week date utilities in `times`:
|
|
||||||
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
|
|
||||||
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
|
|
||||||
- Added a `initDateTime` overload to create a datetime from an ISO week date.
|
|
||||||
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
|
|
||||||
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
|
|
||||||
- Added new modules which were part of `std/os`:
|
|
||||||
- Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling.
|
|
||||||
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
|
|
||||||
- Added `std/cmdline` for reading command line parameters.
|
|
||||||
- Added `sep` parameter in `std/uri` to specify the query separator.
|
|
||||||
- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
|
|
||||||
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
|
|
||||||
in `jscore` for JavaScript targets.
|
|
||||||
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
|
|
||||||
- Added `complex.sgn` for obtaining the phase of complex numbers.
|
|
||||||
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
|
|
||||||
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
|
|
||||||
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
|
|
||||||
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
|
|
||||||
`toggleAttribute`, and `matches` to `std/dom`.
|
|
||||||
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)
|
|
||||||
- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460
|
|
||||||
- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse.
|
|
||||||
- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse.
|
|
||||||
- Added `safe` parameter to `base64.encodeMime`.
|
|
||||||
|
|
||||||
[//]: # "Deprecations:"
|
|
||||||
- Deprecated `selfExe` for Nimscript.
|
|
||||||
- Deprecated `std/sums`.
|
|
||||||
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
|
|
||||||
Now only `byte` and `char` are supported.
|
|
||||||
|
|
||||||
[//]: # "Removals:"
|
|
||||||
- Removed deprecated module `parseopt2`.
|
|
||||||
- Removed deprecated module `sharedstrings`.
|
|
||||||
- Removed deprecated module `dom_extensions`.
|
|
||||||
- Removed deprecated module `LockFreeHash`.
|
|
||||||
- Removed deprecated module `events`.
|
|
||||||
- Removed deprecated `oids.oidToString`.
|
|
||||||
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
|
|
||||||
- Removed deprecated `jsre.test` and `jsre.toString`.
|
|
||||||
- Removed deprecated `math.c_frexp`.
|
|
||||||
- Removed deprecated `` httpcore.`==` ``.
|
|
||||||
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types.
|
|
||||||
- Removed deprecated `osproc.poDemon`, symbol with typo.
|
|
||||||
- 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) 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 accents 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-define-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 be overridden by users.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
|
|
||||||
## 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`).
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import std/[strutils, os, osproc, parseutils, strformat]
|
|
||||||
|
|
||||||
|
|
||||||
proc main() =
|
|
||||||
var msg = ""
|
|
||||||
const cmd = "./koch boot --gc: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()
|
|
||||||
|
|
@ -10,7 +10,7 @@ SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
|
||||||
echo "building from csources: %nim_csources%"
|
echo "building from csources: %nim_csources%"
|
||||||
|
|
||||||
if not exist %nim_csourcesDir% (
|
if not exist %nim_csourcesDir% (
|
||||||
git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
|
git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir%
|
||||||
)
|
)
|
||||||
|
|
||||||
if not exist %nim_csources% (
|
if not exist %nim_csources% (
|
||||||
|
|
|
||||||
31
ci/funs.sh
31
ci/funs.sh
|
|
@ -31,23 +31,6 @@ nimIsCiSkip(){
|
||||||
fi
|
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(){
|
nimDefineVars(){
|
||||||
. config/build_config.txt
|
. config/build_config.txt
|
||||||
nim_csources=bin/nim_csources_$nim_csourcesHash
|
nim_csources=bin/nim_csources_$nim_csourcesHash
|
||||||
|
|
@ -72,12 +55,6 @@ _nimBuildCsourcesIfNeeded(){
|
||||||
makeX=gmake
|
makeX=gmake
|
||||||
elif [ "$unamestr" = 'OpenBSD' ]; then
|
elif [ "$unamestr" = 'OpenBSD' ]; then
|
||||||
makeX=gmake
|
makeX=gmake
|
||||||
elif [ "$unamestr" = 'NetBSD' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
elif [ "$unamestr" = 'CROSSOS' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
elif [ "$unamestr" = 'SunOS' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
else
|
else
|
||||||
makeX=make
|
makeX=make
|
||||||
fi
|
fi
|
||||||
|
|
@ -124,12 +101,8 @@ nimBuildCsourcesIfNeeded(){
|
||||||
else
|
else
|
||||||
# Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g.
|
# 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`.
|
# via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`.
|
||||||
echo_run git clone -q --depth 1 -b $nim_csourcesBranch \
|
echo_run git clone -q --depth 1 $nim_csourcesUrl "$nim_csourcesDir"
|
||||||
$nim_csourcesUrl "$nim_csourcesDir"
|
echo_run git -C "$nim_csourcesDir" checkout $nim_csourcesHash
|
||||||
# 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`
|
# if needed we could also add: `git reset --hard $nim_csourcesHash`
|
||||||
fi
|
fi
|
||||||
_nimBuildCsourcesIfNeeded "$@"
|
_nimBuildCsourcesIfNeeded "$@"
|
||||||
|
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
import
|
import
|
||||||
ast, astalgo, types, trees, intsets
|
ast, astalgo, types, trees, intsets
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TAnalysisResult* = enum
|
TAnalysisResult* = enum
|
||||||
arNo, arMaybe, arYes
|
arNo, arMaybe, arYes
|
||||||
|
|
@ -77,29 +74,24 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
## cases:
|
## cases:
|
||||||
##
|
##
|
||||||
## YES-cases:
|
## YES-cases:
|
||||||
## ```
|
|
||||||
## x <| x # for general trees
|
## x <| x # for general trees
|
||||||
## x[] <| x
|
## x[] <| x
|
||||||
## x[i] <| x
|
## x[i] <| x
|
||||||
## x.f <| x
|
## x.f <| x
|
||||||
## ```
|
|
||||||
##
|
##
|
||||||
## NO-cases:
|
## NO-cases:
|
||||||
## ```
|
|
||||||
## x !<| y # depending on type and symbol kind
|
## x !<| y # depending on type and symbol kind
|
||||||
## x[constA] !<| x[constB]
|
## x[constA] !<| x[constB]
|
||||||
## x.f !<| x.g
|
## x.f !<| x.g
|
||||||
## x.f !<| y.f iff x !<= y
|
## x.f !<| y.f iff x !<= y
|
||||||
## ```
|
|
||||||
##
|
##
|
||||||
## MAYBE-cases:
|
## MAYBE-cases:
|
||||||
##
|
##
|
||||||
## ```
|
|
||||||
## x[] ?<| y[] iff compatible type
|
## x[] ?<| y[] iff compatible type
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## x[] ?<| y depending on type
|
## x[] ?<| y depending on type
|
||||||
## ```
|
##
|
||||||
if a.kind == b.kind:
|
if a.kind == b.kind:
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
|
||||||
302
compiler/ast.nim
302
compiler/ast.nim
|
|
@ -10,12 +10,9 @@
|
||||||
# abstract syntax tree + symbol table
|
# abstract syntax tree + symbol table
|
||||||
|
|
||||||
import
|
import
|
||||||
lineinfos, hashes, options, ropes, idents, int128, tables
|
lineinfos, hashes, options, ropes, idents, int128
|
||||||
from strutils import toLowerAscii
|
from strutils import toLowerAscii
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
export int128
|
export int128
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -207,11 +204,12 @@ type
|
||||||
nkPtrTy, # ``ptr T``
|
nkPtrTy, # ``ptr T``
|
||||||
nkVarTy, # ``var T``
|
nkVarTy, # ``var T``
|
||||||
nkConstTy, # ``const T``
|
nkConstTy, # ``const T``
|
||||||
nkOutTy, # ``out T``
|
nkMutableTy, # ``mutable T``
|
||||||
nkDistinctTy, # distinct type
|
nkDistinctTy, # distinct type
|
||||||
nkProcTy, # proc type
|
nkProcTy, # proc type
|
||||||
nkIteratorTy, # iterator type
|
nkIteratorTy, # iterator type
|
||||||
nkSinkAsgn, # '=sink(x, y)'
|
nkSharedTy, # 'shared T'
|
||||||
|
# we use 'nkPostFix' for the 'not nil' addition
|
||||||
nkEnumTy, # enum body
|
nkEnumTy, # enum body
|
||||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||||
nkArgList, # argument list
|
nkArgList, # argument list
|
||||||
|
|
@ -231,7 +229,7 @@ type
|
||||||
TNodeKinds* = set[TNodeKind]
|
TNodeKinds* = set[TNodeKind]
|
||||||
|
|
||||||
type
|
type
|
||||||
TSymFlag* = enum # 49 flags!
|
TSymFlag* = enum # 46 flags!
|
||||||
sfUsed, # read access of sym (for warnings) or simply used
|
sfUsed, # read access of sym (for warnings) or simply used
|
||||||
sfExported, # symbol is exported from module
|
sfExported, # symbol is exported from module
|
||||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||||
|
|
@ -259,8 +257,7 @@ type
|
||||||
# *OR*: a proc is indirectly called (used as first class)
|
# *OR*: a proc is indirectly called (used as first class)
|
||||||
sfCompilerProc, # proc is a compiler proc, that is a C proc that is
|
sfCompilerProc, # proc is a compiler proc, that is a C proc that is
|
||||||
# needed for the code generator
|
# needed for the code generator
|
||||||
sfEscapes # param escapes
|
sfProcvar, # proc can be passed to a proc var
|
||||||
# currently unimplemented
|
|
||||||
sfDiscriminant, # field is a discriminant in a record/object
|
sfDiscriminant, # field is a discriminant in a record/object
|
||||||
sfRequiresInit, # field must be initialized during construction
|
sfRequiresInit, # field must be initialized during construction
|
||||||
sfDeprecated, # symbol is deprecated
|
sfDeprecated, # symbol is deprecated
|
||||||
|
|
@ -299,17 +296,9 @@ type
|
||||||
sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
|
sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
|
||||||
sfNeverRaises # proc can never raise an exception, not even OverflowDefect
|
sfNeverRaises # proc can never raise an exception, not even OverflowDefect
|
||||||
# or out-of-memory
|
# or out-of-memory
|
||||||
sfSystemRaisesDefect # proc in the system can raise defects
|
|
||||||
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
||||||
sfSingleUsedTemp # For temporaries that we know will only be used once
|
sfSingleUsedTemp # For temporaries that we know will only be used once
|
||||||
sfNoalias # 'noalias' annotation, means C's 'restrict'
|
sfNoalias # 'noalias' annotation, means C's 'restrict'
|
||||||
sfEffectsDelayed # an 'effectsDelayed' parameter
|
|
||||||
sfGeneratedType # A anonymous generic type that is generated by the compiler for
|
|
||||||
# objects that do not have generic parameters in case one of the
|
|
||||||
# object fields has one.
|
|
||||||
#
|
|
||||||
# This is disallowed but can cause the typechecking to go into
|
|
||||||
# an infinite loop, this flag is used as a sentinel to stop it.
|
|
||||||
|
|
||||||
TSymFlags* = set[TSymFlag]
|
TSymFlags* = set[TSymFlag]
|
||||||
|
|
||||||
|
|
@ -338,10 +327,10 @@ const
|
||||||
sfExperimental* = sfOverriden # module uses the .experimental switch
|
sfExperimental* = sfOverriden # module uses the .experimental switch
|
||||||
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||||
sfWrittenTo* = sfBorrow # param is assigned to
|
sfWrittenTo* = sfBorrow # param is assigned to
|
||||||
# currently unimplemented
|
sfEscapes* = sfProcvar # param escapes
|
||||||
sfBase* = sfDiscriminant
|
sfBase* = sfDiscriminant
|
||||||
|
sfIsSelf* = sfOverriden # param is 'self'
|
||||||
sfCustomPragma* = sfRegister # symbol is custom pragma template
|
sfCustomPragma* = sfRegister # symbol is custom pragma template
|
||||||
sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
|
|
||||||
|
|
||||||
const
|
const
|
||||||
# getting ready for the future expr/stmt merge
|
# getting ready for the future expr/stmt merge
|
||||||
|
|
@ -354,8 +343,7 @@ const
|
||||||
ensuresEffects* = 2 # 'ensures' annotation
|
ensuresEffects* = 2 # 'ensures' annotation
|
||||||
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
||||||
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
|
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
|
||||||
forbiddenEffects* = 5 # list of illegal effects
|
effectListLen* = 5 # list of effects list
|
||||||
effectListLen* = 6 # list of effects list
|
|
||||||
nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
||||||
# these must be last statements in a block
|
# these must be last statements in a block
|
||||||
|
|
||||||
|
|
@ -508,12 +496,10 @@ type
|
||||||
# the flag is applied to proc default values and to calls
|
# the flag is applied to proc default values and to calls
|
||||||
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
||||||
nfLastRead # this node is a last read
|
nfLastRead # this node is a last read
|
||||||
nfFirstWrite # this node is a first write
|
nfFirstWrite# this node is a first write
|
||||||
nfHasComment # node has a comment
|
|
||||||
nfUseDefaultField # node has a default value (object constructor)
|
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
TNodeFlags* = set[TNodeFlag]
|
||||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46)
|
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40)
|
||||||
tfVarargs, # procedure has C styled varargs
|
tfVarargs, # procedure has C styled varargs
|
||||||
# tyArray type represeting a varargs list
|
# tyArray type represeting a varargs list
|
||||||
tfNoSideEffect, # procedure type does not allow side effects
|
tfNoSideEffect, # procedure type does not allow side effects
|
||||||
|
|
@ -580,9 +566,6 @@ type
|
||||||
# (for importc types); type is fully specified, allowing to compute
|
# (for importc types); type is fully specified, allowing to compute
|
||||||
# sizeof, alignof, offsetof at CT
|
# sizeof, alignof, offsetof at CT
|
||||||
tfExplicitCallConv
|
tfExplicitCallConv
|
||||||
tfIsConstructor
|
|
||||||
tfEffectSystemWorkaround
|
|
||||||
tfIsOutParam
|
|
||||||
|
|
||||||
TTypeFlags* = set[TTypeFlag]
|
TTypeFlags* = set[TTypeFlag]
|
||||||
|
|
||||||
|
|
@ -633,7 +616,7 @@ const
|
||||||
skError* = skUnknown
|
skError* = skUnknown
|
||||||
|
|
||||||
var
|
var
|
||||||
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
|
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect}
|
||||||
## type flags that are essential for type equality.
|
## type flags that are essential for type equality.
|
||||||
## This is now a variable because for emulation of version:1.0 we
|
## This is now a variable because for emulation of version:1.0 we
|
||||||
## might exclude {tfGcSafe, tfNoSideEffect}.
|
## might exclude {tfGcSafe, tfNoSideEffect}.
|
||||||
|
|
@ -668,9 +651,7 @@ type
|
||||||
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
|
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
|
||||||
mUnaryPlusI, mBitnotI,
|
mUnaryPlusI, mBitnotI,
|
||||||
mUnaryPlusF64, mUnaryMinusF64,
|
mUnaryPlusF64, mUnaryMinusF64,
|
||||||
mCharToStr, mBoolToStr,
|
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
||||||
mIntToStr, mInt64ToStr, mFloatToStr, # for compiling nimStdlibVersion < 1.5.1 (not bootstrapping)
|
|
||||||
mCStrToStr,
|
|
||||||
mStrToStr, mEnumToStr,
|
mStrToStr, mEnumToStr,
|
||||||
mAnd, mOr,
|
mAnd, mOr,
|
||||||
mImplies, mIff, mExists, mForall, mOld,
|
mImplies, mIff, mExists, mForall, mOld,
|
||||||
|
|
@ -683,10 +664,10 @@ type
|
||||||
mInSet, mRepr, mExit,
|
mInSet, mRepr, mExit,
|
||||||
mSetLengthStr, mSetLengthSeq,
|
mSetLengthStr, mSetLengthSeq,
|
||||||
mIsPartOf, mAstToStr, mParallel,
|
mIsPartOf, mAstToStr, mParallel,
|
||||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
mSwap, mIsNil, mArrToSeq,
|
||||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||||
mMove, mWasMoved, mDestroy, mTrace,
|
mMove, mWasMoved, mDestroy,
|
||||||
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
|
mDefault, mUnown, mIsolate, mAccessEnv, mReset,
|
||||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||||
mOrdinal, mIterableType,
|
mOrdinal, mIterableType,
|
||||||
|
|
@ -706,19 +687,19 @@ type
|
||||||
mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
|
mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
|
||||||
|
|
||||||
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
|
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
|
||||||
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetStrVal, mNLineInfo,
|
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
|
||||||
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
|
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
|
||||||
mNBindSym, mNCallSite,
|
mNBindSym, mNCallSite,
|
||||||
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
|
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
|
||||||
mNHint, mNWarning, mNError,
|
mNHint, mNWarning, mNError,
|
||||||
mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
|
mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
|
||||||
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples,
|
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples,
|
||||||
mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
|
mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
|
||||||
mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault
|
mSymIsInstantiationOf, mNodeId, mPrivateAccess
|
||||||
|
|
||||||
|
|
||||||
|
# things that we can evaluate safely at compile time, even if not asked for it:
|
||||||
const
|
const
|
||||||
# things that we can evaluate safely at compile time, even if not asked for it:
|
|
||||||
ctfeWhitelist* = {mNone, mSucc,
|
ctfeWhitelist* = {mNone, mSucc,
|
||||||
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
||||||
mLengthStr, mLengthArray, mLengthSeq,
|
mLengthStr, mLengthArray, mLengthSeq,
|
||||||
|
|
@ -738,27 +719,19 @@ const
|
||||||
mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
|
mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor,
|
||||||
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
|
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
|
||||||
mUnaryPlusF64, mUnaryMinusF64,
|
mUnaryPlusF64, mUnaryMinusF64,
|
||||||
mCharToStr, mBoolToStr,
|
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
||||||
mIntToStr, mInt64ToStr, mFloatToStr,
|
|
||||||
mCStrToStr,
|
|
||||||
mStrToStr, mEnumToStr,
|
mStrToStr, mEnumToStr,
|
||||||
mAnd, mOr,
|
mAnd, mOr,
|
||||||
mEqStr, mLeStr, mLtStr,
|
mEqStr, mLeStr, mLtStr,
|
||||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||||
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||||
mInSet, mRepr, mOpenArrayToSeq}
|
mInSet, mRepr}
|
||||||
|
|
||||||
generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq}
|
|
||||||
## magics that are generated as normal procs in the backend
|
|
||||||
|
|
||||||
type
|
type
|
||||||
ItemId* = object
|
ItemId* = object
|
||||||
module*: int32
|
module*: int32
|
||||||
item*: int32
|
item*: int32
|
||||||
|
|
||||||
proc `$`*(x: ItemId): string =
|
|
||||||
"(module: " & $x.module & ", item: " & $x.item & ")"
|
|
||||||
|
|
||||||
proc `==`*(a, b: ItemId): bool {.inline.} =
|
proc `==`*(a, b: ItemId): bool {.inline.} =
|
||||||
a.item == b.item and a.module == b.module
|
a.item == b.item and a.module == b.module
|
||||||
|
|
||||||
|
|
@ -796,8 +769,7 @@ type
|
||||||
ident*: PIdent
|
ident*: PIdent
|
||||||
else:
|
else:
|
||||||
sons*: TNodeSeq
|
sons*: TNodeSeq
|
||||||
when defined(nimsuggest):
|
comment*: string
|
||||||
endInfo*: TLineInfo
|
|
||||||
|
|
||||||
TStrTable* = object # a table[PIdent] of PSym
|
TStrTable* = object # a table[PIdent] of PSym
|
||||||
counter*: int
|
counter*: int
|
||||||
|
|
@ -883,7 +855,7 @@ type
|
||||||
case kind*: TSymKind
|
case kind*: TSymKind
|
||||||
of routineKinds:
|
of routineKinds:
|
||||||
#procInstCache*: seq[PInstantiation]
|
#procInstCache*: seq[PInstantiation]
|
||||||
gcUnsafetyReason*: PSym # for better error messages regarding gcsafe
|
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
|
||||||
transformedBody*: PNode # cached body after transf pass
|
transformedBody*: PNode # cached body after transf pass
|
||||||
of skLet, skVar, skField, skForVar:
|
of skLet, skVar, skField, skForVar:
|
||||||
guard*: PSym
|
guard*: PSym
|
||||||
|
|
@ -894,8 +866,6 @@ type
|
||||||
typ*: PType
|
typ*: PType
|
||||||
name*: PIdent
|
name*: PIdent
|
||||||
info*: TLineInfo
|
info*: TLineInfo
|
||||||
when defined(nimsuggest):
|
|
||||||
endInfo*: TLineInfo
|
|
||||||
owner*: PSym
|
owner*: PSym
|
||||||
flags*: TSymFlags
|
flags*: TSymFlags
|
||||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||||
|
|
@ -933,13 +903,14 @@ type
|
||||||
allUsages*: seq[TLineInfo]
|
allUsages*: seq[TLineInfo]
|
||||||
|
|
||||||
TTypeSeq* = seq[PType]
|
TTypeSeq* = seq[PType]
|
||||||
|
TLockLevel* = distinct int16
|
||||||
|
|
||||||
TTypeAttachedOp* = enum ## as usual, order is important here
|
TTypeAttachedOp* = enum ## as usual, order is important here
|
||||||
attachedWasMoved,
|
|
||||||
attachedDestructor,
|
attachedDestructor,
|
||||||
attachedAsgn,
|
attachedAsgn,
|
||||||
attachedSink,
|
attachedSink,
|
||||||
attachedTrace,
|
attachedTrace,
|
||||||
|
attachedDispose,
|
||||||
attachedDeepCopy
|
attachedDeepCopy
|
||||||
|
|
||||||
TType* {.acyclic.} = object of TIdObj # \
|
TType* {.acyclic.} = object of TIdObj # \
|
||||||
|
|
@ -967,6 +938,7 @@ type
|
||||||
# -1 means that the size is unkwown
|
# -1 means that the size is unkwown
|
||||||
align*: int16 # the type's alignment requirements
|
align*: int16 # the type's alignment requirements
|
||||||
paddingAtEnd*: int16 #
|
paddingAtEnd*: int16 #
|
||||||
|
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||||
loc*: TLoc
|
loc*: TLoc
|
||||||
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
||||||
# type.
|
# type.
|
||||||
|
|
@ -1015,45 +987,13 @@ type
|
||||||
TImplication* = enum
|
TImplication* = enum
|
||||||
impUnknown, impNo, impYes
|
impUnknown, impNo, impYes
|
||||||
|
|
||||||
template nodeId(n: PNode): int = cast[int](n)
|
|
||||||
|
|
||||||
type Gconfig = object
|
|
||||||
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
|
|
||||||
# reduces memory usage given that `PNode` is the most allocated type by far.
|
|
||||||
comments: Table[int, string] # nodeId => comment
|
|
||||||
useIc*: bool
|
|
||||||
|
|
||||||
var gconfig {.threadvar.}: Gconfig
|
|
||||||
|
|
||||||
proc setUseIc*(useIc: bool) = gconfig.useIc = useIc
|
|
||||||
|
|
||||||
proc comment*(n: PNode): string =
|
|
||||||
if nfHasComment in n.flags and not gconfig.useIc:
|
|
||||||
# IC doesn't track comments, see `packed_ast`, so this could fail
|
|
||||||
result = gconfig.comments[n.nodeId]
|
|
||||||
|
|
||||||
proc `comment=`*(n: PNode, a: string) =
|
|
||||||
let id = n.nodeId
|
|
||||||
if a.len > 0:
|
|
||||||
# if needed, we could periodically cleanup gconfig.comments when its size increases,
|
|
||||||
# to ensure only live nodes (and with nfHasComment) have an entry in gconfig.comments;
|
|
||||||
# for compiling compiler, the waste is very small:
|
|
||||||
# num calls to newNodeImpl: 14984160 (num of PNode allocations)
|
|
||||||
# size of gconfig.comments: 33585
|
|
||||||
# num of nodes with comments that were deleted and hence wasted: 3081
|
|
||||||
n.flags.incl nfHasComment
|
|
||||||
gconfig.comments[id] = a
|
|
||||||
elif nfHasComment in n.flags:
|
|
||||||
n.flags.excl nfHasComment
|
|
||||||
gconfig.comments.del(id)
|
|
||||||
|
|
||||||
# BUGFIX: a module is overloadable so that a proc can have the
|
# BUGFIX: a module is overloadable so that a proc can have the
|
||||||
# same name as an imported module. This is necessary because of
|
# same name as an imported module. This is necessary because of
|
||||||
# the poor naming choices in the standard library.
|
# the poor naming choices in the standard library.
|
||||||
|
|
||||||
const
|
const
|
||||||
OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
|
OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
|
||||||
skConverter, skModule, skTemplate, skMacro, skEnumField}
|
skConverter, skModule, skTemplate, skMacro}
|
||||||
|
|
||||||
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
||||||
tyGenericParam}
|
tyGenericParam}
|
||||||
|
|
@ -1080,8 +1020,7 @@ const
|
||||||
nfDotSetter, nfDotField,
|
nfDotSetter, nfDotField,
|
||||||
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
||||||
nfFromTemplate, nfDefaultRefsParam,
|
nfFromTemplate, nfDefaultRefsParam,
|
||||||
nfExecuteOnReload, nfLastRead,
|
nfExecuteOnReload, nfLastRead, nfFirstWrite}
|
||||||
nfFirstWrite, nfUseDefaultField}
|
|
||||||
namePos* = 0
|
namePos* = 0
|
||||||
patternPos* = 1 # empty except for term rewriting macros
|
patternPos* = 1 # empty except for term rewriting macros
|
||||||
genericParamsPos* = 2
|
genericParamsPos* = 2
|
||||||
|
|
@ -1125,9 +1064,24 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||||
# which may simplify code.
|
# which may simplify code.
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym: a.sym.name
|
of nkSym: a.sym.name
|
||||||
of nkIdent: a.ident
|
of nkIdent: a.ident
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
|
proc getnimblePkg*(a: PSym): PSym =
|
||||||
|
result = a
|
||||||
|
while result != nil:
|
||||||
|
case result.kind
|
||||||
|
of skModule:
|
||||||
|
result = result.owner
|
||||||
|
assert result.kind == skPackage
|
||||||
|
of skPackage:
|
||||||
|
if result.owner == nil:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
result = result.owner
|
||||||
|
else:
|
||||||
|
assert false, $result.kind
|
||||||
|
|
||||||
const
|
const
|
||||||
moduleShift = when defined(cpu32): 20 else: 24
|
moduleShift = when defined(cpu32): 20 else: 24
|
||||||
|
|
||||||
|
|
@ -1172,7 +1126,13 @@ when false:
|
||||||
assert dest.ItemId.item <= src.ItemId.item
|
assert dest.ItemId.item <= src.ItemId.item
|
||||||
dest = src
|
dest = src
|
||||||
|
|
||||||
|
proc getnimblePkgId*(a: PSym): int =
|
||||||
|
let b = a.getnimblePkg
|
||||||
|
result = if b == nil: -1 else: b.id
|
||||||
|
|
||||||
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
||||||
|
#var
|
||||||
|
# gMainPackageId*: int
|
||||||
|
|
||||||
proc isCallExpr*(n: PNode): bool =
|
proc isCallExpr*(n: PNode): bool =
|
||||||
result = n.kind in nkCallKinds
|
result = n.kind in nkCallKinds
|
||||||
|
|
@ -1208,77 +1168,13 @@ template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x
|
||||||
template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
|
template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
|
||||||
template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x
|
template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x
|
||||||
|
|
||||||
proc getDeclPragma*(n: PNode): PNode =
|
|
||||||
## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found.
|
|
||||||
## Currently only supports routineDefs + {nkTypeDef}.
|
|
||||||
case n.kind
|
|
||||||
of routineDefs:
|
|
||||||
if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos]
|
|
||||||
of nkTypeDef:
|
|
||||||
#[
|
|
||||||
type F3*{.deprecated: "x3".} = int
|
|
||||||
|
|
||||||
TypeSection
|
|
||||||
TypeDef
|
|
||||||
PragmaExpr
|
|
||||||
Postfix
|
|
||||||
Ident "*"
|
|
||||||
Ident "F3"
|
|
||||||
Pragma
|
|
||||||
ExprColonExpr
|
|
||||||
Ident "deprecated"
|
|
||||||
StrLit "x3"
|
|
||||||
Empty
|
|
||||||
Ident "int"
|
|
||||||
]#
|
|
||||||
if n[0].kind == nkPragmaExpr:
|
|
||||||
result = n[0][1]
|
|
||||||
else:
|
|
||||||
# support as needed for `nkIdentDefs` etc.
|
|
||||||
result = nil
|
|
||||||
if result != nil:
|
|
||||||
assert result.kind == nkPragma, $(result.kind, n.kind)
|
|
||||||
|
|
||||||
proc extractPragma*(s: PSym): PNode =
|
|
||||||
## gets the pragma node of routine/type/var/let/const symbol `s`
|
|
||||||
if s.kind in routineKinds:
|
|
||||||
result = s.ast[pragmasPos]
|
|
||||||
elif s.kind in {skType, skVar, skLet, skConst}:
|
|
||||||
if s.ast != nil and s.ast.len > 0:
|
|
||||||
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
|
|
||||||
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
|
|
||||||
result = s.ast[0][1]
|
|
||||||
assert result == nil or result.kind == nkPragma
|
|
||||||
|
|
||||||
proc skipPragmaExpr*(n: PNode): PNode =
|
|
||||||
## if pragma expr, give the node the pragmas are applied to,
|
|
||||||
## otherwise give node itself
|
|
||||||
if n.kind == nkPragmaExpr:
|
|
||||||
result = n[0]
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
|
|
||||||
proc setInfoRecursive*(n: PNode, info: TLineInfo) =
|
|
||||||
## set line info recursively
|
|
||||||
if n != nil:
|
|
||||||
for i in 0..<n.safeLen: setInfoRecursive(n[i], info)
|
|
||||||
n.info = info
|
|
||||||
|
|
||||||
when defined(useNodeIds):
|
when defined(useNodeIds):
|
||||||
const nodeIdToDebug* = -1 # 2322968
|
const nodeIdToDebug* = -1 # 2322968
|
||||||
var gNodeId: int
|
var gNodeId: int
|
||||||
|
|
||||||
template newNodeImpl(info2) =
|
proc newNode*(kind: TNodeKind): PNode =
|
||||||
result = PNode(kind: kind, info: info2)
|
## new node with unknown line info, no type, and no children
|
||||||
when false:
|
result = PNode(kind: kind, info: unknownLineInfo)
|
||||||
# this would add overhead, so we skip it; it results in a small amount of leaked entries
|
|
||||||
# for old PNode that gets re-allocated at the same address as a PNode that
|
|
||||||
# has `nfHasComment` set (and an entry in that table). Only `nfHasComment`
|
|
||||||
# should be used to test whether a PNode has a comment; gconfig.comments
|
|
||||||
# can contain extra entries for deleted PNode's with comments.
|
|
||||||
gconfig.comments.del(cast[int](result))
|
|
||||||
|
|
||||||
template setIdMaybe() =
|
|
||||||
when defined(useNodeIds):
|
when defined(useNodeIds):
|
||||||
result.id = gNodeId
|
result.id = gNodeId
|
||||||
if result.id == nodeIdToDebug:
|
if result.id == nodeIdToDebug:
|
||||||
|
|
@ -1286,22 +1182,27 @@ template setIdMaybe() =
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
inc gNodeId
|
inc gNodeId
|
||||||
|
|
||||||
proc newNode*(kind: TNodeKind): PNode =
|
|
||||||
## new node with unknown line info, no type, and no children
|
|
||||||
newNodeImpl(unknownLineInfo)
|
|
||||||
setIdMaybe()
|
|
||||||
|
|
||||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
|
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
|
||||||
## new node with line info, no type, and no children
|
## new node with line info, no type, and no children
|
||||||
newNodeImpl(info)
|
result = PNode(kind: kind, info: info)
|
||||||
setIdMaybe()
|
when defined(useNodeIds):
|
||||||
|
result.id = gNodeId
|
||||||
|
if result.id == nodeIdToDebug:
|
||||||
|
echo "KIND ", result.kind
|
||||||
|
writeStackTrace()
|
||||||
|
inc gNodeId
|
||||||
|
|
||||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
|
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
|
||||||
## new node with line info, type, and children
|
## new node with line info, type, and children
|
||||||
newNodeImpl(info)
|
result = PNode(kind: kind, info: info)
|
||||||
if children > 0:
|
if children > 0:
|
||||||
newSeq(result.sons, children)
|
newSeq(result.sons, children)
|
||||||
setIdMaybe()
|
when defined(useNodeIds):
|
||||||
|
result.id = gNodeId
|
||||||
|
if result.id == nodeIdToDebug:
|
||||||
|
echo "KIND ", result.kind
|
||||||
|
writeStackTrace()
|
||||||
|
inc gNodeId
|
||||||
|
|
||||||
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||||
## new node with line info, type, and no children
|
## new node with line info, type, and no children
|
||||||
|
|
@ -1352,7 +1253,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
|
||||||
|
|
||||||
proc astdef*(s: PSym): PNode =
|
proc astdef*(s: PSym): PNode =
|
||||||
# get only the definition (initializer) portion of the ast
|
# get only the definition (initializer) portion of the ast
|
||||||
if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
|
if s.ast != nil and s.ast.kind == nkIdentDefs:
|
||||||
s.ast[2]
|
s.ast[2]
|
||||||
else:
|
else:
|
||||||
s.ast
|
s.ast
|
||||||
|
|
@ -1502,8 +1403,16 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
||||||
pragmas, exceptions, body]
|
pragmas, exceptions, body]
|
||||||
|
|
||||||
const
|
const
|
||||||
|
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
||||||
|
MaxLockLevel* = 1000'i16
|
||||||
|
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||||
"=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
|
"=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"]
|
||||||
|
|
||||||
|
proc `$`*(x: TLockLevel): string =
|
||||||
|
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||||
|
elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
|
||||||
|
else: result = $int16(x)
|
||||||
|
|
||||||
proc `$`*(s: PSym): string =
|
proc `$`*(s: PSym): string =
|
||||||
if s != nil:
|
if s != nil:
|
||||||
|
|
@ -1514,6 +1423,7 @@ proc `$`*(s: PSym): string =
|
||||||
proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
||||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||||
align: defaultAlignment, itemId: id,
|
align: defaultAlignment, itemId: id,
|
||||||
|
lockLevel: UnspecifiedLockLevel,
|
||||||
uniqueId: id)
|
uniqueId: id)
|
||||||
when false:
|
when false:
|
||||||
if result.itemId.module == 55 and result.itemId.item == 2:
|
if result.itemId.module == 55 and result.itemId.item == 2:
|
||||||
|
|
@ -1526,7 +1436,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||||
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
||||||
a.flags.incl b.flags
|
a.flags.incl b.flags
|
||||||
if a.lode == nil: a.lode = b.lode
|
if a.lode == nil: a.lode = b.lode
|
||||||
if a.r == "": a.r = b.r
|
if a.r == nil: a.r = b.r
|
||||||
|
|
||||||
proc newSons*(father: Indexable, length: int) =
|
proc newSons*(father: Indexable, length: int) =
|
||||||
setLen(father.sons, length)
|
setLen(father.sons, length)
|
||||||
|
|
@ -1538,10 +1448,11 @@ proc assignType*(dest, src: PType) =
|
||||||
dest.n = src.n
|
dest.n = src.n
|
||||||
dest.size = src.size
|
dest.size = src.size
|
||||||
dest.align = src.align
|
dest.align = src.align
|
||||||
|
dest.lockLevel = src.lockLevel
|
||||||
# this fixes 'type TLock = TSysLock':
|
# this fixes 'type TLock = TSysLock':
|
||||||
if src.sym != nil:
|
if src.sym != nil:
|
||||||
if dest.sym != nil:
|
if dest.sym != nil:
|
||||||
dest.sym.flags.incl src.sym.flags-{sfUsed, sfExported}
|
dest.sym.flags.incl src.sym.flags-{sfExported}
|
||||||
if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
|
if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
|
||||||
mergeLoc(dest.sym.loc, src.sym.loc)
|
mergeLoc(dest.sym.loc, src.sym.loc)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1649,7 +1560,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||||
if mask != {} and propagateHasAsgn:
|
if mask != {} and propagateHasAsgn:
|
||||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||||
tySequence, tySet, tyDistinct}:
|
tySequence, tySet, tyDistinct, tyOpenArray, tyVarargs}:
|
||||||
o2.flags.incl mask
|
o2.flags.incl mask
|
||||||
owner.flags.incl mask
|
owner.flags.incl mask
|
||||||
|
|
||||||
|
|
@ -1695,13 +1606,11 @@ proc copyNode*(src: PNode): PNode =
|
||||||
of nkIdent: result.ident = src.ident
|
of nkIdent: result.ident = src.ident
|
||||||
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
|
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
|
||||||
else: discard
|
else: discard
|
||||||
when defined(nimsuggest):
|
|
||||||
result.endInfo = src.endInfo
|
|
||||||
|
|
||||||
template transitionNodeKindCommon(k: TNodeKind) =
|
template transitionNodeKindCommon(k: TNodeKind) =
|
||||||
let obj {.inject.} = n[]
|
let obj {.inject.} = n[]
|
||||||
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
|
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags,
|
||||||
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
|
comment: obj.comment)
|
||||||
when defined(useNodeIds):
|
when defined(useNodeIds):
|
||||||
n.id = obj.id
|
n.id = obj.id
|
||||||
|
|
||||||
|
|
@ -1713,10 +1622,6 @@ proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) =
|
||||||
transitionNodeKindCommon(kind)
|
transitionNodeKindCommon(kind)
|
||||||
n.intVal = obj.intVal
|
n.intVal = obj.intVal
|
||||||
|
|
||||||
proc transitionIntToFloatKind*(n: PNode, kind: range[nkFloatLit..nkFloat128Lit]) =
|
|
||||||
transitionNodeKindCommon(kind)
|
|
||||||
n.floatVal = BiggestFloat(obj.intVal)
|
|
||||||
|
|
||||||
proc transitionNoneToSym*(n: PNode) =
|
proc transitionNoneToSym*(n: PNode) =
|
||||||
transitionNodeKindCommon(nkSym)
|
transitionNodeKindCommon(nkSym)
|
||||||
|
|
||||||
|
|
@ -1749,8 +1654,6 @@ template copyNodeImpl(dst, src, processSonsStmt) =
|
||||||
if src == nil: return
|
if src == nil: return
|
||||||
dst = newNode(src.kind)
|
dst = newNode(src.kind)
|
||||||
dst.info = src.info
|
dst.info = src.info
|
||||||
when defined(nimsuggest):
|
|
||||||
result.endInfo = src.endInfo
|
|
||||||
dst.typ = src.typ
|
dst.typ = src.typ
|
||||||
dst.flags = src.flags * PersistentNodeFlags
|
dst.flags = src.flags * PersistentNodeFlags
|
||||||
dst.comment = src.comment
|
dst.comment = src.comment
|
||||||
|
|
@ -1808,7 +1711,7 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
|
||||||
|
|
||||||
proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
|
proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkEmpty..nkNilLit, nkFormalParams: result = n.kind == kind
|
of nkEmpty..nkNilLit: result = n.kind == kind
|
||||||
else:
|
else:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
if (n[i].kind == kind) or hasSubnodeWith(n[i], kind):
|
if (n[i].kind == kind) or hasSubnodeWith(n[i], kind):
|
||||||
|
|
@ -1984,26 +1887,6 @@ proc toObject*(typ: PType): PType =
|
||||||
if t.kind == tyRef: t.lastSon
|
if t.kind == tyRef: t.lastSon
|
||||||
else: typ
|
else: typ
|
||||||
|
|
||||||
proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
|
||||||
#[
|
|
||||||
See also `toObject`.
|
|
||||||
Finds the underlying `object`, even in cases like these:
|
|
||||||
type
|
|
||||||
B[T] = object f0: int
|
|
||||||
A1[T] = ref B[T]
|
|
||||||
A2[T] = ref object f1: int
|
|
||||||
A3 = ref object f2: int
|
|
||||||
A4 = object f3: int
|
|
||||||
]#
|
|
||||||
result = typ
|
|
||||||
while true:
|
|
||||||
case result.kind
|
|
||||||
of tyGenericBody: result = result.lastSon
|
|
||||||
of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
|
|
||||||
# automatic dereferencing is deep, refs #18298.
|
|
||||||
else: break
|
|
||||||
assert result.sym != nil
|
|
||||||
|
|
||||||
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||||
assert t != nil
|
assert t != nil
|
||||||
|
|
||||||
|
|
@ -2056,9 +1939,6 @@ template detailedInfo*(sym: PSym): string =
|
||||||
proc isInlineIterator*(typ: PType): bool {.inline.} =
|
proc isInlineIterator*(typ: PType): bool {.inline.} =
|
||||||
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
|
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
|
||||||
|
|
||||||
proc isIterator*(typ: PType): bool {.inline.} =
|
|
||||||
typ.kind == tyProc and tfIterator in typ.flags
|
|
||||||
|
|
||||||
proc isClosureIterator*(typ: PType): bool {.inline.} =
|
proc isClosureIterator*(typ: PType): bool {.inline.} =
|
||||||
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
|
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
|
||||||
|
|
||||||
|
|
@ -2129,13 +2009,3 @@ proc skipAddr*(n: PNode): PNode {.inline.} =
|
||||||
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
|
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
|
||||||
assert n.kind == nkTypeClassTy
|
assert n.kind == nkTypeClassTy
|
||||||
result = n[0].kind == nkEmpty
|
result = n[0].kind == nkEmpty
|
||||||
|
|
||||||
proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags
|
|
||||||
|
|
||||||
const
|
|
||||||
nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit,
|
|
||||||
nkTypeSection, nkProcDef, nkConverterDef,
|
|
||||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
|
||||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
|
||||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
|
||||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt}
|
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,9 @@
|
||||||
# the data structures here are used in various places of the compiler.
|
# the data structures here are used in various places of the compiler.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
|
ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
|
||||||
msgs
|
msgs
|
||||||
|
|
||||||
import strutils except addf
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc hashNode*(p: RootRef): Hash
|
proc hashNode*(p: RootRef): Hash
|
||||||
proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
|
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
|
# Convert a tree into its YAML representation; this is used by the
|
||||||
|
|
@ -226,10 +221,11 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
|
||||||
## Named parameters are special because a named parameter can be
|
## Named parameters are special because a named parameter can be
|
||||||
## gensym'ed and then they have '\`<number>' suffix that we need to
|
## gensym'ed and then they have '\`<number>' suffix that we need to
|
||||||
## ignore, see compiler / evaltempl.nim, snippet:
|
## ignore, see compiler / evaltempl.nim, snippet:
|
||||||
## ```
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
|
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
|
||||||
## if c.instLines: actual.info else: templ.info)
|
## if c.instLines: actual.info else: templ.info)
|
||||||
## ```
|
|
||||||
for i in 1..<list.len:
|
for i in 1..<list.len:
|
||||||
let it = list[i].sym
|
let it = list[i].sym
|
||||||
if it.name.id == ident.id or
|
if it.name.id == ident.id or
|
||||||
|
|
@ -257,7 +253,7 @@ proc makeYamlString*(s: string): Rope =
|
||||||
# this could trigger InternalError(111). See the ropes module for
|
# this could trigger InternalError(111). See the ropes module for
|
||||||
# further information.
|
# further information.
|
||||||
const MaxLineLength = 64
|
const MaxLineLength = 64
|
||||||
result = ""
|
result = nil
|
||||||
var res = "\""
|
var res = "\""
|
||||||
for i in 0..<s.len:
|
for i in 0..<s.len:
|
||||||
if (i + 1) mod MaxLineLength == 0:
|
if (i + 1) mod MaxLineLength == 0:
|
||||||
|
|
@ -273,9 +269,9 @@ proc flagsToStr[T](flags: set[T]): Rope =
|
||||||
if flags == {}:
|
if flags == {}:
|
||||||
result = rope("[]")
|
result = rope("[]")
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = nil
|
||||||
for x in items(flags):
|
for x in items(flags):
|
||||||
if result != "": result.add(", ")
|
if result != nil: result.add(", ")
|
||||||
result.add(makeYamlString($x))
|
result.add(makeYamlString($x))
|
||||||
result = "[" & result & "]"
|
result = "[" & result & "]"
|
||||||
|
|
||||||
|
|
@ -330,7 +326,7 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
|
||||||
sonsRope = rope("null")
|
sonsRope = rope("null")
|
||||||
elif containsOrIncl(marker, n.id):
|
elif containsOrIncl(marker, n.id):
|
||||||
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
|
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
|
||||||
strutils.toHex(cast[int](n), sizeof(n) * 2))]
|
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
|
||||||
else:
|
else:
|
||||||
if n.len > 0:
|
if n.len > 0:
|
||||||
sonsRope = rope("[")
|
sonsRope = rope("[")
|
||||||
|
|
@ -589,9 +585,6 @@ proc value(this: var DebugPrinter; value: PNode) =
|
||||||
this.openCurly
|
this.openCurly
|
||||||
this.key "kind"
|
this.key "kind"
|
||||||
this.value value.kind
|
this.value value.kind
|
||||||
if value.comment.len > 0:
|
|
||||||
this.key "comment"
|
|
||||||
this.value value.comment
|
|
||||||
when defined(useNodeIds):
|
when defined(useNodeIds):
|
||||||
this.key "id"
|
this.key "id"
|
||||||
this.value value.id
|
this.value value.id
|
||||||
|
|
@ -766,9 +759,9 @@ proc strTableAdd*(t: var TStrTable, n: PSym) =
|
||||||
|
|
||||||
proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
|
proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
|
||||||
onConflictKeepOld = false): PSym =
|
onConflictKeepOld = false): PSym =
|
||||||
# if `t` has a conflicting symbol (same identifier as `n`), return it
|
# returns true if n is already in the string table:
|
||||||
# otherwise return `nil`. Incl `n` to `t` unless `onConflictKeepOld = true`
|
# It is essential that `n` is written nevertheless!
|
||||||
# and a conflict was found.
|
# This way the newest redefinition is picked by the semantic analyses!
|
||||||
assert n.name != nil
|
assert n.name != nil
|
||||||
var h: Hash = n.name.h and high(t.data)
|
var h: Hash = n.name.h and high(t.data)
|
||||||
var replaceSlot = -1
|
var replaceSlot = -1
|
||||||
|
|
@ -784,10 +777,9 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym;
|
||||||
replaceSlot = h
|
replaceSlot = h
|
||||||
h = nextTry(h, high(t.data))
|
h = nextTry(h, high(t.data))
|
||||||
if replaceSlot >= 0:
|
if replaceSlot >= 0:
|
||||||
result = t.data[replaceSlot] # found it
|
|
||||||
if not onConflictKeepOld:
|
if not onConflictKeepOld:
|
||||||
t.data[replaceSlot] = n # overwrite it with newer definition!
|
t.data[replaceSlot] = n # overwrite it with newer definition!
|
||||||
return result # but return the old one
|
return t.data[replaceSlot] # found it
|
||||||
elif mustRehash(t.data.len, t.counter):
|
elif mustRehash(t.data.len, t.counter):
|
||||||
strTableEnlarge(t)
|
strTableEnlarge(t)
|
||||||
strTableRawInsert(t.data, n)
|
strTableRawInsert(t.data, n)
|
||||||
|
|
@ -816,21 +808,16 @@ type
|
||||||
name*: PIdent
|
name*: PIdent
|
||||||
|
|
||||||
proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
|
proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||||
# hot spots
|
|
||||||
var h = ti.h and high(tab.data)
|
var h = ti.h and high(tab.data)
|
||||||
var start = h
|
var start = h
|
||||||
var p {.cursor.} = tab.data[h]
|
result = tab.data[h]
|
||||||
while p != nil:
|
while result != nil:
|
||||||
if p.name.id == ti.name.id: break
|
if result.name.id == ti.name.id: break
|
||||||
h = nextTry(h, high(tab.data))
|
h = nextTry(h, high(tab.data))
|
||||||
if h == start:
|
if h == start:
|
||||||
p = nil
|
result = nil
|
||||||
break
|
break
|
||||||
p = tab.data[h]
|
result = tab.data[h]
|
||||||
if p != nil:
|
|
||||||
result = p # increase the count
|
|
||||||
else:
|
|
||||||
result = nil
|
|
||||||
ti.h = nextTry(h, high(tab.data))
|
ti.h = nextTry(h, high(tab.data))
|
||||||
|
|
||||||
proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
|
proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@
|
||||||
import std/strutils
|
import std/strutils
|
||||||
import options, ast, msgs
|
import options, ast, msgs
|
||||||
|
|
||||||
proc typSym*(t: PType): PSym =
|
|
||||||
result = t.sym
|
|
||||||
if result == nil and t.kind == tyGenericInst: # this might need to be refined
|
|
||||||
result = t[0].sym
|
|
||||||
|
|
||||||
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
|
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
|
||||||
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
|
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
|
||||||
|
|
||||||
|
|
@ -18,7 +13,7 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||||
# xxx figure out how to resolve `tyGenericParam`, e.g. for
|
# xxx figure out how to resolve `tyGenericParam`, e.g. for
|
||||||
# proc fn[T](a: T, b: T) = discard
|
# proc fn[T](a: T, b: T) = discard
|
||||||
# fn(1.1, "a")
|
# fn(1.1, "a")
|
||||||
let typ = typ.skipTypes(abstractInst + {tyStatic, tySequence, tyArray, tySet, tyUserTypeClassInst, tyVar, tyRef, tyPtr} - {tyRange})
|
let typ = typ.skipTypes(abstractInst + {tyStatic} - {tyRange})
|
||||||
result.add " [$1" % typ.kind.toHumanStr
|
result.add " [$1" % typ.kind.toHumanStr
|
||||||
if typ.sym != nil:
|
if typ.sym != nil:
|
||||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||||
|
|
@ -26,14 +21,3 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||||
|
|
||||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||||
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
|
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,25 +0,0 @@
|
||||||
import pragmas, options, ast, trees
|
|
||||||
|
|
||||||
proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
|
||||||
optionsStack.add options
|
|
||||||
|
|
||||||
proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
|
||||||
options = optionsStack[^1]
|
|
||||||
optionsStack.setLen(optionsStack.len-1)
|
|
||||||
|
|
||||||
proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions,
|
|
||||||
n: PNode, start: int) =
|
|
||||||
pushBackendOption(optionsStack, options)
|
|
||||||
for i in start..<n.len:
|
|
||||||
let it = n[i]
|
|
||||||
if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit:
|
|
||||||
let sw = whichPragma(it[0])
|
|
||||||
let opts = pragmaToOptions(sw)
|
|
||||||
if opts != {}:
|
|
||||||
if it[1].intVal != 0:
|
|
||||||
options.incl opts
|
|
||||||
else:
|
|
||||||
options.excl opts
|
|
||||||
|
|
||||||
template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) =
|
|
||||||
popBackendOption(optionsStack, options)
|
|
||||||
|
|
@ -10,9 +10,6 @@
|
||||||
# this unit handles Nim sets; it implements bit sets
|
# this unit handles Nim sets; it implements bit sets
|
||||||
# the code here should be reused in the Nim standard library
|
# the code here should be reused in the Nim standard library
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
ElemType = byte
|
ElemType = byte
|
||||||
TBitSet* = seq[ElemType] # we use byte here to avoid issues with
|
TBitSet* = seq[ElemType] # we use byte here to avoid issues with
|
||||||
|
|
@ -26,40 +23,53 @@ const
|
||||||
template modElemSize(arg: untyped): untyped = arg and 7
|
template modElemSize(arg: untyped): untyped = arg and 7
|
||||||
template divElemSize(arg: untyped): untyped = arg shr 3
|
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
|
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)
|
assert(elem >= 0)
|
||||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
|
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
|
||||||
(One shl elem.modElemSize)
|
(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
|
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
|
||||||
not(One shl elem.modElemSize)
|
not(One shl elem.modElemSize)
|
||||||
|
|
||||||
proc bitSetInit*(b: var TBitSet, length: int) =
|
proc bitSetInit(b: var TBitSet, length: int) =
|
||||||
newSeq(b, length)
|
newSeq(b, length)
|
||||||
|
|
||||||
proc bitSetUnion*(x: var TBitSet, y: TBitSet) =
|
proc bitSetUnion(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] or y[i]
|
for i in 0..high(x): x[i] = x[i] or y[i]
|
||||||
|
|
||||||
proc bitSetDiff*(x: var TBitSet, y: TBitSet) =
|
proc bitSetDiff(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] and not y[i]
|
for i in 0..high(x): x[i] = x[i] and not y[i]
|
||||||
|
|
||||||
proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) =
|
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] xor y[i]
|
for i in 0..high(x): x[i] = x[i] xor y[i]
|
||||||
|
|
||||||
proc bitSetIntersect*(x: var TBitSet, y: TBitSet) =
|
proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] and y[i]
|
for i in 0..high(x): x[i] = x[i] and y[i]
|
||||||
|
|
||||||
proc bitSetEquals*(x, y: TBitSet): bool =
|
proc bitSetEquals(x, y: TBitSet): bool =
|
||||||
for i in 0..high(x):
|
for i in 0..high(x):
|
||||||
if x[i] != y[i]:
|
if x[i] != y[i]:
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc bitSetContains*(x, y: TBitSet): bool =
|
proc bitSetContains(x, y: TBitSet): bool =
|
||||||
for i in 0..high(x):
|
for i in 0..high(x):
|
||||||
if (x[i] and not y[i]) != Zero:
|
if (x[i] and not y[i]) != Zero:
|
||||||
return false
|
return false
|
||||||
|
|
@ -86,6 +96,6 @@ const populationCount: array[uint8, uint8] = block:
|
||||||
|
|
||||||
arr
|
arr
|
||||||
|
|
||||||
proc bitSetCard*(x: TBitSet): BiggestInt =
|
proc bitSetCard(x: TBitSet): BiggestInt =
|
||||||
for it in x:
|
for it in x:
|
||||||
result.inc int(populationCount[it])
|
result.inc int(populationCount[it])
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@
|
||||||
## BTree implementation with few features, but good enough for the
|
## BTree implementation with few features, but good enough for the
|
||||||
## Nim compiler's needs.
|
## Nim compiler's needs.
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
const
|
const
|
||||||
M = 512 # max children per B-tree node = M-1
|
M = 512 # max children per B-tree node = M-1
|
||||||
# (must be even and greater than 2)
|
# (must be even and greater than 2)
|
||||||
|
|
@ -68,10 +65,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
|
||||||
result.links[j] = h.links[Mhalf + j]
|
result.links[j] = h.links[Mhalf + j]
|
||||||
else:
|
else:
|
||||||
for j in 0..<Mhalf:
|
for j in 0..<Mhalf:
|
||||||
when defined(gcArc) or defined(gcOrc):
|
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||||
result.vals[j] = move h.vals[Mhalf + j]
|
|
||||||
else:
|
|
||||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
|
||||||
|
|
||||||
proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
|
proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
|
||||||
## split node in half
|
## split node in half
|
||||||
|
|
@ -91,10 +85,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
|
||||||
if less(key, h.keys[j]): break
|
if less(key, h.keys[j]): break
|
||||||
inc j
|
inc j
|
||||||
for i in countdown(h.entries, j+1):
|
for i in countdown(h.entries, j+1):
|
||||||
when defined(gcArc) or defined(gcOrc):
|
shallowCopy(h.vals[i], h.vals[i-1])
|
||||||
h.vals[i] = move h.vals[i-1]
|
|
||||||
else:
|
|
||||||
shallowCopy(h.vals[i], h.vals[i-1])
|
|
||||||
h.vals[j] = val
|
h.vals[j] = val
|
||||||
else:
|
else:
|
||||||
var newLink: Node[Key, Val] = nil
|
var newLink: Node[Key, Val] = nil
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,14 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||||
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
|
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
|
||||||
result = false
|
result = false
|
||||||
elif optPanics in p.config.globalOptions or
|
elif optPanics in p.config.globalOptions or
|
||||||
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
|
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags):
|
||||||
sfSystemRaisesDefect notin n.sym.flags):
|
|
||||||
# we know we can be strict:
|
# we know we can be strict:
|
||||||
result = canRaise(n)
|
result = canRaise(n)
|
||||||
else:
|
else:
|
||||||
# we have to be *very* conservative:
|
# we have to be *very* conservative:
|
||||||
result = canRaiseConservative(n)
|
result = canRaiseConservative(n)
|
||||||
|
|
||||||
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
proc preventNrvo(p: BProc; le, ri: PNode): bool =
|
||||||
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
||||||
var n = le
|
var n = le
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -55,11 +54,6 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||||
if canRaise(ri[0]) and
|
if canRaise(ri[0]) and
|
||||||
locationEscapes(p, le, p.nestedTryStmts.len > 0):
|
locationEscapes(p, le, p.nestedTryStmts.len > 0):
|
||||||
message(p.config, le.info, warnObservableStores, $le)
|
message(p.config, le.info, warnObservableStores, $le)
|
||||||
# bug #19613 prevent dangerous aliasing too:
|
|
||||||
if dest != nil and dest != le:
|
|
||||||
for i in 1..<ri.len:
|
|
||||||
let r = ri[i]
|
|
||||||
if isPartOf(dest, r) != arNo: return true
|
|
||||||
|
|
||||||
proc hasNoInit(call: PNode): bool {.inline.} =
|
proc hasNoInit(call: PNode): bool {.inline.} =
|
||||||
result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
|
result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
|
||||||
|
|
@ -78,32 +72,32 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||||
callee, params: Rope) =
|
callee, params: Rope) =
|
||||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||||
genLineDir(p, ri)
|
genLineDir(p, ri)
|
||||||
var pl = callee & "(" & params
|
var pl = callee & ~"(" & params
|
||||||
# getUniqueType() is too expensive here:
|
# getUniqueType() is too expensive here:
|
||||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||||
if typ[0] != nil:
|
if typ[0] != nil:
|
||||||
if isInvalidReturnType(p.config, typ):
|
if isInvalidReturnType(p.config, typ[0]):
|
||||||
if params.len != 0: pl.add(", ")
|
if params != nil: pl.add(~", ")
|
||||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri):
|
||||||
# Great, we can use 'd':
|
# Great, we can use 'd':
|
||||||
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
||||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||||
# reset before pass as 'result' var:
|
# reset before pass as 'result' var:
|
||||||
discard "resetLoc(p, d)"
|
discard "resetLoc(p, d)"
|
||||||
pl.add(addrLoc(p.config, d))
|
pl.add(addrLoc(p.config, d))
|
||||||
pl.add(");\n")
|
pl.add(~");$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
else:
|
else:
|
||||||
var tmp: TLoc
|
var tmp: TLoc
|
||||||
getTemp(p, typ[0], tmp, needsInit=true)
|
getTemp(p, typ[0], tmp, needsInit=true)
|
||||||
pl.add(addrLoc(p.config, tmp))
|
pl.add(addrLoc(p.config, tmp))
|
||||||
pl.add(");\n")
|
pl.add(~");$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||||
if canRaise: raiseExit(p)
|
if canRaise: raiseExit(p)
|
||||||
else:
|
else:
|
||||||
pl.add(")")
|
pl.add(~")")
|
||||||
if p.module.compileToCpp:
|
if p.module.compileToCpp:
|
||||||
if lfSingleUse in d.flags:
|
if lfSingleUse in d.flags:
|
||||||
# do not generate spurious temporaries for C++! For C we're better off
|
# do not generate spurious temporaries for C++! For C we're better off
|
||||||
|
|
@ -141,7 +135,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||||
if canRaise: raiseExit(p)
|
if canRaise: raiseExit(p)
|
||||||
genAssignment(p, d, tmp, {})
|
genAssignment(p, d, tmp, {})
|
||||||
else:
|
else:
|
||||||
pl.add(");\n")
|
pl.add(~");$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
if canRaise: raiseExit(p)
|
if canRaise: raiseExit(p)
|
||||||
|
|
||||||
|
|
@ -156,7 +150,7 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} =
|
||||||
else:
|
else:
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
|
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) =
|
||||||
var a, b, c: TLoc
|
var a, b, c: TLoc
|
||||||
initLocExpr(p, q[1], a)
|
initLocExpr(p, q[1], a)
|
||||||
initLocExpr(p, q[2], b)
|
initLocExpr(p, q[2], b)
|
||||||
|
|
@ -164,8 +158,6 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||||
# but first produce the required index checks:
|
# but first produce the required index checks:
|
||||||
if optBoundsCheck in p.options:
|
if optBoundsCheck in p.options:
|
||||||
genBoundsCheck(p, a, b, c)
|
genBoundsCheck(p, a, b, c)
|
||||||
if prepareForMutation:
|
|
||||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
|
||||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||||
let dest = getTypeDesc(p.module, destType)
|
let dest = getTypeDesc(p.module, destType)
|
||||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||||
|
|
@ -176,10 +168,8 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||||
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
||||||
lengthExpr)
|
lengthExpr)
|
||||||
else:
|
else:
|
||||||
var lit = newRopeAppender()
|
|
||||||
intLiteral(first, lit)
|
|
||||||
result = ("($4*)($1)+(($2)-($3))" %
|
result = ("($4*)($1)+(($2)-($3))" %
|
||||||
[rdLoc(a), rdLoc(b), lit, dest],
|
[rdLoc(a), rdLoc(b), intLiteral(first), dest],
|
||||||
lengthExpr)
|
lengthExpr)
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
if reifiedOpenArray(q[1]):
|
if reifiedOpenArray(q[1]):
|
||||||
|
|
@ -197,17 +187,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||||
optSeqDestructors in p.config.globalOptions:
|
optSeqDestructors in p.config.globalOptions:
|
||||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||||
if atyp.kind in {tyVar} and not compileToCpp(p.module):
|
if atyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||||
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
|
result = ("($4*)(*$1)$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest],
|
||||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
|
|
||||||
lengthExpr)
|
lengthExpr)
|
||||||
else:
|
else:
|
||||||
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
|
result = ("($4*)$1$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest],
|
||||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
|
|
||||||
lengthExpr)
|
lengthExpr)
|
||||||
else:
|
else:
|
||||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||||
|
|
||||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
|
||||||
var q = skipConv(n)
|
var q = skipConv(n)
|
||||||
var skipped = false
|
var skipped = false
|
||||||
while q.kind == nkStmtListExpr and q.len > 0:
|
while q.kind == nkStmtListExpr and q.len > 0:
|
||||||
|
|
@ -222,19 +210,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||||
genStmts(p, q[i])
|
genStmts(p, q[i])
|
||||||
q = q.lastSon
|
q = q.lastSon
|
||||||
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
|
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
|
||||||
result.add x & ", " & y
|
result = x & ", " & y
|
||||||
else:
|
else:
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
|
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
|
||||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
case skipTypes(a.t, abstractVar).kind
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
if reifiedOpenArray(n):
|
if reifiedOpenArray(n):
|
||||||
if a.t.kind in {tyVar, tyLent}:
|
if a.t.kind in {tyVar, tyLent}:
|
||||||
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
|
result = "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||||
else:
|
else:
|
||||||
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
|
result = "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||||
else:
|
else:
|
||||||
result.add "$1, $1Len_0" % [rdLoc(a)]
|
result = "$1, $1Len_0" % [rdLoc(a)]
|
||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
let ntyp = skipTypes(n.typ, abstractInst)
|
let ntyp = skipTypes(n.typ, abstractInst)
|
||||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
||||||
|
|
@ -243,24 +231,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||||
var t: TLoc
|
var t: TLoc
|
||||||
t.r = "(*$1)" % [a.rdLoc]
|
t.r = "(*$1)" % [a.rdLoc]
|
||||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
|
||||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
|
||||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
|
||||||
else:
|
else:
|
||||||
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
|
result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
|
||||||
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
|
|
||||||
of tyArray:
|
of tyArray:
|
||||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||||
of tyPtr, tyRef:
|
of tyPtr, tyRef:
|
||||||
case lastSon(a.t).kind
|
case lastSon(a.t).kind
|
||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
var t: TLoc
|
var t: TLoc
|
||||||
t.r = "(*$1)" % [a.rdLoc]
|
t.r = "(*$1)" % [a.rdLoc]
|
||||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
|
||||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
|
||||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
|
||||||
of tyArray:
|
of tyArray:
|
||||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
|
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
|
||||||
else:
|
else:
|
||||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||||
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||||
|
|
@ -280,24 +263,24 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||||
getTemp(p, a.lode.typ, result, needsInit=false)
|
getTemp(p, a.lode.typ, result, needsInit=false)
|
||||||
genAssignment(p, result, a, {})
|
genAssignment(p, result, a, {})
|
||||||
|
|
||||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
|
proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, n[0], a)
|
initLocExpr(p, n[0], a)
|
||||||
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||||
|
|
||||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
|
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
if n.kind == nkStringToCString:
|
if n.kind == nkStringToCString:
|
||||||
genArgStringToCString(p, n, result, needsTmp)
|
result = genArgStringToCString(p, n, needsTmp)
|
||||||
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||||
var n = if n.kind != nkHiddenAddr: n else: n[0]
|
var n = if n.kind != nkHiddenAddr: n else: n[0]
|
||||||
openArrayLoc(p, param.typ, n, result)
|
result = openArrayLoc(p, param.typ, n)
|
||||||
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
|
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
|
||||||
initLocExpr(p, n, a)
|
initLocExpr(p, n, a)
|
||||||
if n.kind in {nkCharLit..nkNilLit}:
|
if n.kind in {nkCharLit..nkNilLit}:
|
||||||
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
|
result = addrLoc(p.config, literalsNeedsTmp(p, a))
|
||||||
else:
|
else:
|
||||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp))
|
||||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||||
n.kind == nkHiddenAddr:
|
n.kind == nkHiddenAddr:
|
||||||
initLocExprSingleUse(p, n[0], a)
|
initLocExprSingleUse(p, n[0], a)
|
||||||
|
|
@ -307,23 +290,23 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||||
if callee.kind == nkSym and
|
if callee.kind == nkSym and
|
||||||
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
||||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||||
addAddrLoc(p.config, a, result)
|
result = addrLoc(p.config, a)
|
||||||
else:
|
else:
|
||||||
addRdLoc(a, result)
|
result = rdLoc(a)
|
||||||
else:
|
else:
|
||||||
initLocExprSingleUse(p, n, a)
|
initLocExprSingleUse(p, n, a)
|
||||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||||
#assert result != nil
|
#assert result != nil
|
||||||
|
|
||||||
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
|
proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
if n.kind == nkStringToCString:
|
if n.kind == nkStringToCString:
|
||||||
genArgStringToCString(p, n, result, needsTmp)
|
result = genArgStringToCString(p, n, needsTmp)
|
||||||
else:
|
else:
|
||||||
initLocExprSingleUse(p, n, a)
|
initLocExprSingleUse(p, n, a)
|
||||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||||
|
|
||||||
import aliasanalysis
|
from dfa import aliases, AliasKind
|
||||||
|
|
||||||
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
|
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
|
||||||
for p in potentialWrites:
|
for p in potentialWrites:
|
||||||
|
|
@ -334,50 +317,38 @@ proc skipTrivialIndirections(n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
while true:
|
while true:
|
||||||
case result.kind
|
case result.kind
|
||||||
of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv:
|
of {nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv}:
|
||||||
result = result[0]
|
result = result[0]
|
||||||
of nkHiddenStdConv, nkHiddenSubConv:
|
of {nkHiddenStdConv, nkHiddenSubConv}:
|
||||||
result = result[1]
|
result = result[1]
|
||||||
else: break
|
else: break
|
||||||
|
|
||||||
proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
|
proc getPotentialWrites(n: PNode, mutate = false): seq[PNode] =
|
||||||
case n.kind:
|
case n.kind:
|
||||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
of nkLiterals, nkIdent: discard
|
||||||
of nkSym:
|
of nkSym:
|
||||||
if mutate: result.add n
|
if mutate: result.add n
|
||||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
getPotentialWrites(n[0], true, result)
|
result.add getPotentialWrites(n[0], true)
|
||||||
getPotentialWrites(n[1], mutate, result)
|
result.add getPotentialWrites(n[1], mutate)
|
||||||
of nkAddr, nkHiddenAddr:
|
of nkAddr, nkHiddenAddr:
|
||||||
getPotentialWrites(n[0], true, result)
|
result.add getPotentialWrites(n[0], true)
|
||||||
of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr:
|
of nkCallKinds: #TODO: Find out why in f += 1, f is a nkSym and not a nkHiddenAddr
|
||||||
getPotentialWrites(n[0], mutate, result)
|
for s in n.sons:
|
||||||
of nkCallKinds:
|
result.add getPotentialWrites(s, true)
|
||||||
case n.getMagic:
|
|
||||||
of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
|
||||||
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset:
|
|
||||||
getPotentialWrites(n[1], true, result)
|
|
||||||
for i in 2..<n.len:
|
|
||||||
getPotentialWrites(n[i], mutate, result)
|
|
||||||
of mSwap:
|
|
||||||
for i in 1..<n.len:
|
|
||||||
getPotentialWrites(n[i], true, result)
|
|
||||||
else:
|
|
||||||
for i in 1..<n.len:
|
|
||||||
getPotentialWrites(n[i], mutate, result)
|
|
||||||
else:
|
else:
|
||||||
for s in n:
|
for s in n.sons:
|
||||||
getPotentialWrites(s, mutate, result)
|
result.add getPotentialWrites(s, mutate)
|
||||||
|
|
||||||
proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
proc getPotentialReads(n: PNode): seq[PNode] =
|
||||||
case n.kind:
|
case n.kind:
|
||||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
of nkLiterals, nkIdent: discard
|
||||||
of nkSym: result.add n
|
of nkSym: result.add n
|
||||||
else:
|
else:
|
||||||
for s in n:
|
for s in n.sons:
|
||||||
getPotentialReads(s, result)
|
result.add getPotentialReads(s)
|
||||||
|
|
||||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
proc genParams(p: BProc, ri: PNode, typ: PType): Rope =
|
||||||
# We must generate temporaries in cases like #14396
|
# We must generate temporaries in cases like #14396
|
||||||
# to keep the strict Left-To-Right evaluation
|
# to keep the strict Left-To-Right evaluation
|
||||||
var needTmp = newSeq[bool](ri.len - 1)
|
var needTmp = newSeq[bool](ri.len - 1)
|
||||||
|
|
@ -386,32 +357,24 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||||
if ri[i].skipTrivialIndirections.kind == nkSym:
|
if ri[i].skipTrivialIndirections.kind == nkSym:
|
||||||
needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
|
needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
|
||||||
else:
|
else:
|
||||||
#if not ri[i].typ.isCompileTimeOnly:
|
for n in getPotentialReads(ri[i]):
|
||||||
var potentialReads: seq[PNode]
|
|
||||||
getPotentialReads(ri[i], potentialReads)
|
|
||||||
for n in potentialReads:
|
|
||||||
if not needTmp[i - 1]:
|
if not needTmp[i - 1]:
|
||||||
needTmp[i - 1] = potentialAlias(n, potentialWrites)
|
needTmp[i - 1] = potentialAlias(n, potentialWrites)
|
||||||
getPotentialWrites(ri[i], false, potentialWrites)
|
potentialWrites.add getPotentialWrites(ri[i])
|
||||||
if ri[i].kind in {nkHiddenAddr, nkAddr}:
|
if ri[i].kind == nkHiddenAddr:
|
||||||
# Optimization: don't use a temp, if we would only take the address anyway
|
# Optimization: don't use a temp, if we would only take the adress anyway
|
||||||
needTmp[i - 1] = false
|
needTmp[i - 1] = false
|
||||||
|
|
||||||
var oldLen = result.len
|
|
||||||
for i in 1..<ri.len:
|
for i in 1..<ri.len:
|
||||||
if i < typ.len:
|
if i < typ.len:
|
||||||
assert(typ.n[i].kind == nkSym)
|
assert(typ.n[i].kind == nkSym)
|
||||||
let paramType = typ.n[i]
|
let paramType = typ.n[i]
|
||||||
if not paramType.typ.isCompileTimeOnly:
|
if not paramType.typ.isCompileTimeOnly:
|
||||||
if oldLen != result.len:
|
if result != nil: result.add(~", ")
|
||||||
result.add(", ")
|
result.add(genArg(p, ri[i], paramType.sym, ri, needTmp[i-1]))
|
||||||
oldLen = result.len
|
|
||||||
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
|
|
||||||
else:
|
else:
|
||||||
if oldLen != result.len:
|
if result != nil: result.add(~", ")
|
||||||
result.add(", ")
|
result.add(genArgNoParam(p, ri[i], needTmp[i-1]))
|
||||||
oldLen = result.len
|
|
||||||
genArgNoParam(p, ri[i], result, needTmp[i-1])
|
|
||||||
|
|
||||||
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||||
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
||||||
|
|
@ -427,8 +390,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
assert(typ.kind == tyProc)
|
assert(typ.kind == tyProc)
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
|
|
||||||
var params = newRopeAppender()
|
var params = genParams(p, ri, typ)
|
||||||
genParams(p, ri, typ, params)
|
|
||||||
|
|
||||||
var callee = rdLoc(op)
|
var callee = rdLoc(op)
|
||||||
if p.hcrOn and ri[0].kind == nkSym:
|
if p.hcrOn and ri[0].kind == nkSym:
|
||||||
|
|
@ -438,7 +400,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
|
|
||||||
proc addComma(r: Rope): Rope =
|
proc addComma(r: Rope): Rope =
|
||||||
if r.len == 0: r else: r & ", "
|
if r == nil: r else: r & ~", "
|
||||||
|
|
||||||
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
|
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
|
||||||
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
|
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
|
||||||
|
|
@ -451,8 +413,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
assert(typ.kind == tyProc)
|
assert(typ.kind == tyProc)
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
|
|
||||||
var pl = newRopeAppender()
|
var pl = genParams(p, ri, typ)
|
||||||
genParams(p, ri, typ, pl)
|
|
||||||
|
|
||||||
template genCallPattern {.dirty.} =
|
template genCallPattern {.dirty.} =
|
||||||
if tfIterator in typ.flags:
|
if tfIterator in typ.flags:
|
||||||
|
|
@ -463,10 +424,10 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
let rawProc = getClosureType(p.module, typ, clHalf)
|
let rawProc = getClosureType(p.module, typ, clHalf)
|
||||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||||
if typ[0] != nil:
|
if typ[0] != nil:
|
||||||
if isInvalidReturnType(p.config, typ):
|
if isInvalidReturnType(p.config, typ[0]):
|
||||||
if ri.len > 1: pl.add(", ")
|
if ri.len > 1: pl.add(~", ")
|
||||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri):
|
||||||
# Great, we can use 'd':
|
# Great, we can use 'd':
|
||||||
if d.k == locNone:
|
if d.k == locNone:
|
||||||
getTemp(p, typ[0], d, needsInit=true)
|
getTemp(p, typ[0], d, needsInit=true)
|
||||||
|
|
@ -510,30 +471,24 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
genCallPattern()
|
genCallPattern()
|
||||||
if canRaise: raiseExit(p)
|
if canRaise: raiseExit(p)
|
||||||
|
|
||||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||||
argsCounter: var int) =
|
|
||||||
if i < typ.len:
|
if i < typ.len:
|
||||||
# 'var T' is 'T&' in C++. This means we ignore the request of
|
# 'var T' is 'T&' in C++. This means we ignore the request of
|
||||||
# any nkHiddenAddr when it's a 'var T'.
|
# any nkHiddenAddr when it's a 'var T'.
|
||||||
let paramType = typ.n[i]
|
let paramType = typ.n[i]
|
||||||
assert(paramType.kind == nkSym)
|
assert(paramType.kind == nkSym)
|
||||||
if paramType.typ.isCompileTimeOnly:
|
if paramType.typ.isCompileTimeOnly:
|
||||||
discard
|
result = nil
|
||||||
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
||||||
if argsCounter > 0: result.add ", "
|
result = genArgNoParam(p, ri[i][0])
|
||||||
genArgNoParam(p, ri[i][0], result)
|
|
||||||
inc argsCounter
|
|
||||||
else:
|
else:
|
||||||
if argsCounter > 0: result.add ", "
|
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
|
||||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
|
||||||
inc argsCounter
|
|
||||||
else:
|
else:
|
||||||
if tfVarargs notin typ.flags:
|
if tfVarargs notin typ.flags:
|
||||||
localError(p.config, ri.info, "wrong argument count")
|
localError(p.config, ri.info, "wrong argument count")
|
||||||
|
result = nil
|
||||||
else:
|
else:
|
||||||
if argsCounter > 0: result.add ", "
|
result = genArgNoParam(p, ri[i])
|
||||||
genArgNoParam(p, ri[i], result)
|
|
||||||
inc argsCounter
|
|
||||||
|
|
||||||
discard """
|
discard """
|
||||||
Dot call syntax in C++
|
Dot call syntax in C++
|
||||||
|
|
@ -590,7 +545,7 @@ proc skipAddrDeref(node: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = node
|
result = node
|
||||||
|
|
||||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||||
# for better or worse c2nim translates the 'this' argument to a 'var T'.
|
# for better or worse c2nim translates the 'this' argument to a 'var T'.
|
||||||
# However manual wrappers may also use 'ptr T'. In any case we support both
|
# However manual wrappers may also use 'ptr T'. In any case we support both
|
||||||
# for convenience.
|
# for convenience.
|
||||||
|
|
@ -604,72 +559,75 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||||
if t.kind in {tyVar}:
|
if t.kind in {tyVar}:
|
||||||
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
|
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
|
||||||
if x.typ.kind == tyPtr:
|
if x.typ.kind == tyPtr:
|
||||||
genArgNoParam(p, x, result)
|
result = genArgNoParam(p, x)
|
||||||
result.add("->")
|
result.add("->")
|
||||||
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
|
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
|
||||||
genArgNoParam(p, x[0], result)
|
result = genArgNoParam(p, x[0])
|
||||||
result.add("->")
|
result.add("->")
|
||||||
else:
|
else:
|
||||||
genArgNoParam(p, x, result)
|
result = genArgNoParam(p, x)
|
||||||
result.add(".")
|
result.add(".")
|
||||||
elif t.kind == tyPtr:
|
elif t.kind == tyPtr:
|
||||||
if ri.kind in {nkAddr, nkHiddenAddr}:
|
if ri.kind in {nkAddr, nkHiddenAddr}:
|
||||||
genArgNoParam(p, ri[0], result)
|
result = genArgNoParam(p, ri[0])
|
||||||
result.add(".")
|
result.add(".")
|
||||||
else:
|
else:
|
||||||
genArgNoParam(p, ri, result)
|
result = genArgNoParam(p, ri)
|
||||||
result.add("->")
|
result.add("->")
|
||||||
else:
|
else:
|
||||||
ri = skipAddrDeref(ri)
|
ri = skipAddrDeref(ri)
|
||||||
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
|
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
|
||||||
genArgNoParam(p, ri, result) #, typ.n[i].sym)
|
result = genArgNoParam(p, ri) #, typ.n[i].sym)
|
||||||
result.add(".")
|
result.add(".")
|
||||||
|
|
||||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
|
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = 1
|
var j = 1
|
||||||
while i < pat.len:
|
while i < pat.len:
|
||||||
case pat[i]
|
case pat[i]
|
||||||
of '@':
|
of '@':
|
||||||
var argsCounter = 0
|
var first = true
|
||||||
for k in j..<ri.len:
|
for k in j..<ri.len:
|
||||||
genOtherArg(p, ri, k, typ, result, argsCounter)
|
let arg = genOtherArg(p, ri, k, typ)
|
||||||
|
if arg.len > 0:
|
||||||
|
if not first:
|
||||||
|
result.add(~", ")
|
||||||
|
first = false
|
||||||
|
result.add arg
|
||||||
inc i
|
inc i
|
||||||
of '#':
|
of '#':
|
||||||
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
||||||
let ri = ri[j]
|
let ri = ri[j]
|
||||||
if ri.kind in nkCallKinds:
|
if ri.kind in nkCallKinds:
|
||||||
let typ = skipTypes(ri[0].typ, abstractInst)
|
let typ = skipTypes(ri[0].typ, abstractInst)
|
||||||
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
|
if pat[i+1] == '+': result.add genArgNoParam(p, ri[0])
|
||||||
result.add("(")
|
result.add(~"(")
|
||||||
if 1 < ri.len:
|
if 1 < ri.len:
|
||||||
var argsCounterB = 0
|
result.add genOtherArg(p, ri, 1, typ)
|
||||||
genOtherArg(p, ri, 1, typ, result, argsCounterB)
|
|
||||||
for k in j+1..<ri.len:
|
for k in j+1..<ri.len:
|
||||||
var argsCounterB = 0
|
result.add(~", ")
|
||||||
genOtherArg(p, ri, k, typ, result, argsCounterB)
|
result.add genOtherArg(p, ri, k, typ)
|
||||||
result.add(")")
|
result.add(~")")
|
||||||
else:
|
else:
|
||||||
localError(p.config, ri.info, "call expression expected for C++ pattern")
|
localError(p.config, ri.info, "call expression expected for C++ pattern")
|
||||||
inc i
|
inc i
|
||||||
elif i+1 < pat.len and pat[i+1] == '.':
|
elif i+1 < pat.len and pat[i+1] == '.':
|
||||||
genThisArg(p, ri, j, typ, result)
|
result.add genThisArg(p, ri, j, typ)
|
||||||
inc i
|
inc i
|
||||||
elif i+1 < pat.len and pat[i+1] == '[':
|
elif i+1 < pat.len and pat[i+1] == '[':
|
||||||
var arg = ri[j].skipAddrDeref
|
var arg = ri[j].skipAddrDeref
|
||||||
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
|
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
|
||||||
genArgNoParam(p, arg, result)
|
result.add genArgNoParam(p, arg)
|
||||||
#result.add debugTree(arg, 0, 10)
|
#result.add debugTree(arg, 0, 10)
|
||||||
else:
|
else:
|
||||||
var argsCounter = 0
|
result.add genOtherArg(p, ri, j, typ)
|
||||||
genOtherArg(p, ri, j, typ, result, argsCounter)
|
|
||||||
inc j
|
inc j
|
||||||
inc i
|
inc i
|
||||||
of '\'':
|
of '\'':
|
||||||
var idx, stars: int
|
var idx, stars: int
|
||||||
if scanCppGenericSlot(pat, i, idx, stars):
|
if scanCppGenericSlot(pat, i, idx, stars):
|
||||||
var t = resolveStarsInCppType(typ, idx, stars)
|
var t = resolveStarsInCppType(typ, idx, stars)
|
||||||
if t == nil: result.add("void")
|
if t == nil: result.add(~"void")
|
||||||
else: result.add(getTypeDesc(p.module, t))
|
else: result.add(getTypeDesc(p.module, t))
|
||||||
else:
|
else:
|
||||||
let start = i
|
let start = i
|
||||||
|
|
@ -687,11 +645,10 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
assert(typ.kind == tyProc)
|
assert(typ.kind == tyProc)
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
# don't call '$' here for efficiency:
|
# don't call '$' here for efficiency:
|
||||||
let pat = $ri[0].sym.loc.r
|
let pat = ri[0].sym.loc.r.data
|
||||||
internalAssert p.config, pat.len > 0
|
internalAssert p.config, pat.len > 0
|
||||||
if pat.contains({'#', '(', '@', '\''}):
|
if pat.contains({'#', '(', '@', '\''}):
|
||||||
var pl = newRopeAppender()
|
var pl = genPatternCall(p, ri, pat, typ)
|
||||||
genPatternCall(p, ri, pat, typ, pl)
|
|
||||||
# simpler version of 'fixupCall' that works with the pl+params combination:
|
# simpler version of 'fixupCall' that works with the pl+params combination:
|
||||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||||
if typ[0] != nil:
|
if typ[0] != nil:
|
||||||
|
|
@ -710,79 +667,80 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
list.r = pl
|
list.r = pl
|
||||||
genAssignment(p, d, list, {}) # no need for deep copying
|
genAssignment(p, d, list, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
pl.add(";\n")
|
pl.add(~";$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
else:
|
else:
|
||||||
var pl = newRopeAppender()
|
var pl: Rope = nil
|
||||||
var argsCounter = 0
|
#var param = typ.n[1].sym
|
||||||
if 1 < ri.len:
|
if 1 < ri.len:
|
||||||
genThisArg(p, ri, 1, typ, pl)
|
pl.add(genThisArg(p, ri, 1, typ))
|
||||||
pl.add(op.r)
|
pl.add(op.r)
|
||||||
var params = newRopeAppender()
|
var params: Rope
|
||||||
for i in 2..<ri.len:
|
for i in 2..<ri.len:
|
||||||
|
if params != nil: params.add(~", ")
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
genOtherArg(p, ri, i, typ, params, argsCounter)
|
params.add(genOtherArg(p, ri, i, typ))
|
||||||
fixupCall(p, le, ri, d, pl, params)
|
fixupCall(p, le, ri, d, pl, params)
|
||||||
|
|
||||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||||
# generates a crappy ObjC call
|
# generates a crappy ObjC call
|
||||||
var op: TLoc
|
var op: TLoc
|
||||||
initLocExpr(p, ri[0], op)
|
initLocExpr(p, ri[0], op)
|
||||||
var pl = "["
|
var pl = ~"["
|
||||||
# getUniqueType() is too expensive here:
|
# getUniqueType() is too expensive here:
|
||||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||||
assert(typ.kind == tyProc)
|
assert(typ.kind == tyProc)
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
|
|
||||||
# don't call '$' here for efficiency:
|
# don't call '$' here for efficiency:
|
||||||
let pat = $ri[0].sym.loc.r
|
let pat = ri[0].sym.loc.r.data
|
||||||
internalAssert p.config, pat.len > 0
|
internalAssert p.config, pat.len > 0
|
||||||
var start = 3
|
var start = 3
|
||||||
if ' ' in pat:
|
if ' ' in pat:
|
||||||
start = 1
|
start = 1
|
||||||
pl.add(op.r)
|
pl.add(op.r)
|
||||||
if ri.len > 1:
|
if ri.len > 1:
|
||||||
pl.add(": ")
|
pl.add(~": ")
|
||||||
genArg(p, ri[1], typ.n[1].sym, ri, pl)
|
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
|
||||||
start = 2
|
start = 2
|
||||||
else:
|
else:
|
||||||
if ri.len > 1:
|
if ri.len > 1:
|
||||||
genArg(p, ri[1], typ.n[1].sym, ri, pl)
|
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
|
||||||
pl.add(" ")
|
pl.add(~" ")
|
||||||
pl.add(op.r)
|
pl.add(op.r)
|
||||||
if ri.len > 2:
|
if ri.len > 2:
|
||||||
pl.add(": ")
|
pl.add(~": ")
|
||||||
genArg(p, ri[2], typ.n[2].sym, ri, pl)
|
pl.add(genArg(p, ri[2], typ.n[2].sym, ri))
|
||||||
for i in start..<ri.len:
|
for i in start..<ri.len:
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
if i >= typ.len:
|
if i >= typ.len:
|
||||||
internalError(p.config, ri.info, "varargs for objective C method?")
|
internalError(p.config, ri.info, "varargs for objective C method?")
|
||||||
assert(typ.n[i].kind == nkSym)
|
assert(typ.n[i].kind == nkSym)
|
||||||
var param = typ.n[i].sym
|
var param = typ.n[i].sym
|
||||||
pl.add(" ")
|
pl.add(~" ")
|
||||||
pl.add(param.name.s)
|
pl.add(param.name.s)
|
||||||
pl.add(": ")
|
pl.add(~": ")
|
||||||
genArg(p, ri[i], param, ri, pl)
|
pl.add(genArg(p, ri[i], param, ri))
|
||||||
if typ[0] != nil:
|
if typ[0] != nil:
|
||||||
if isInvalidReturnType(p.config, typ):
|
if isInvalidReturnType(p.config, typ[0]):
|
||||||
if ri.len > 1: pl.add(" ")
|
if ri.len > 1: pl.add(~" ")
|
||||||
# beware of 'result = p(result)'. We always allocate a temporary:
|
# beware of 'result = p(result)'. We always allocate a temporary:
|
||||||
if d.k in {locTemp, locNone}:
|
if d.k in {locTemp, locNone}:
|
||||||
# We already got a temp. Great, special case it:
|
# We already got a temp. Great, special case it:
|
||||||
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
||||||
pl.add("Result: ")
|
pl.add(~"Result: ")
|
||||||
pl.add(addrLoc(p.config, d))
|
pl.add(addrLoc(p.config, d))
|
||||||
pl.add("];\n")
|
pl.add(~"];$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
else:
|
else:
|
||||||
var tmp: TLoc
|
var tmp: TLoc
|
||||||
getTemp(p, typ[0], tmp, needsInit=true)
|
getTemp(p, typ[0], tmp, needsInit=true)
|
||||||
pl.add(addrLoc(p.config, tmp))
|
pl.add(addrLoc(p.config, tmp))
|
||||||
pl.add("];\n")
|
pl.add(~"];$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
pl.add("]")
|
pl.add(~"]")
|
||||||
if d.k == locNone: getTemp(p, typ[0], d)
|
if d.k == locNone: getTemp(p, typ[0], d)
|
||||||
assert(d.t != nil) # generate an assignment to d:
|
assert(d.t != nil) # generate an assignment to d:
|
||||||
var list: TLoc
|
var list: TLoc
|
||||||
|
|
@ -790,7 +748,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||||
list.r = pl
|
list.r = pl
|
||||||
genAssignment(p, d, list, {}) # no need for deep copying
|
genAssignment(p, d, list, {}) # no need for deep copying
|
||||||
else:
|
else:
|
||||||
pl.add("];\n")
|
pl.add(~"];$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
|
|
||||||
proc notYetAlive(n: PNode): bool {.inline.} =
|
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||||
|
|
@ -828,5 +786,6 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
genNamedParamCall(p, ri, d)
|
genNamedParamCall(p, ri, d)
|
||||||
else:
|
else:
|
||||||
genPrefixCall(p, le, ri, d)
|
genPrefixCall(p, le, ri, d)
|
||||||
|
postStmtActions(p)
|
||||||
|
|
||||||
proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d)
|
proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -21,7 +21,7 @@ template detectVersion(field, corename) =
|
||||||
if core == nil or core.kind != skConst:
|
if core == nil or core.kind != skConst:
|
||||||
m.g.field = 1
|
m.g.field = 1
|
||||||
else:
|
else:
|
||||||
m.g.field = toInt(ast.getInt(core.astdef))
|
m.g.field = toInt(ast.getInt(core.ast))
|
||||||
result = m.g.field
|
result = m.g.field
|
||||||
|
|
||||||
proc detectStrVersion(m: BModule): int =
|
proc detectStrVersion(m: BModule): int =
|
||||||
|
|
@ -32,87 +32,82 @@ proc detectSeqVersion(m: BModule): int =
|
||||||
|
|
||||||
# ----- Version 1: GC'ed strings and seqs --------------------------------
|
# ----- Version 1: GC'ed strings and seqs --------------------------------
|
||||||
|
|
||||||
proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
|
||||||
cgsym(m, "TGenericSeq")
|
discard cgsym(m, "TGenericSeq")
|
||||||
let tmp = getTempName(m)
|
result = getTempName(m)
|
||||||
result.add tmp
|
m.s[cfsData].addf("STRING_LITERAL($1, $2, $3);$n",
|
||||||
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
|
[result, makeCString(s), rope(s.len)])
|
||||||
[tmp, makeCString(s), rope(s.len)])
|
|
||||||
|
|
||||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
proc genStringLiteralV1(m: BModule; n: PNode): Rope =
|
||||||
if s.isNil:
|
if s.isNil:
|
||||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
|
||||||
else:
|
else:
|
||||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||||
if id == m.labels:
|
if id == m.labels:
|
||||||
# string literal not found in the cache:
|
# string literal not found in the cache:
|
||||||
appcg(m, result, "((#NimStringDesc*) &", [])
|
result = ropecg(m, "((#NimStringDesc*) &$1)",
|
||||||
genStringLiteralDataOnlyV1(m, n.strVal, result)
|
[genStringLiteralDataOnlyV1(m, n.strVal)])
|
||||||
result.add ")"
|
|
||||||
else:
|
else:
|
||||||
appcg(m, result, "((#NimStringDesc*) &$1$2)",
|
result = ropecg(m, "((#NimStringDesc*) &$1$2)",
|
||||||
[m.tmpBase, id])
|
[m.tmpBase, id])
|
||||||
|
|
||||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
# ------ Version 2: destructor based strings and seqs -----------------------
|
||||||
|
|
||||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
||||||
m.s[cfsStrData].addf("static $4 struct {$n" &
|
m.s[cfsData].addf("static $4 struct {$n" &
|
||||||
" NI cap; NIM_CHAR data[$2+1];$n" &
|
" NI cap; NIM_CHAR data[$2+1];$n" &
|
||||||
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
|
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
|
||||||
[result, rope(s.len), makeCString(s),
|
[result, rope(s.len), makeCString(s),
|
||||||
rope(if isConst: "const" else: "")])
|
rope(if isConst: "const" else: "")])
|
||||||
|
|
||||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): Rope =
|
||||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||||
if id == m.labels:
|
if id == m.labels:
|
||||||
let pureLit = getTempName(m)
|
let pureLit = getTempName(m)
|
||||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||||
let tmp = getTempName(m)
|
result = getTempName(m)
|
||||||
result.add tmp
|
discard cgsym(m, "NimStrPayload")
|
||||||
cgsym(m, "NimStrPayload")
|
discard cgsym(m, "NimStringV2")
|
||||||
cgsym(m, "NimStringV2")
|
|
||||||
# string literal not found in the cache:
|
# string literal not found in the cache:
|
||||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||||
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
[result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
||||||
else:
|
else:
|
||||||
let tmp = getTempName(m)
|
result = getTempName(m)
|
||||||
result.add tmp
|
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
[result, rope(n.strVal.len), m.tmpBase & rope(id),
|
||||||
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
|
|
||||||
rope(if isConst: "const" else: "")])
|
rope(if isConst: "const" else: "")])
|
||||||
|
|
||||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope =
|
||||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||||
var pureLit: Rope
|
var pureLit: Rope
|
||||||
if id == m.labels:
|
if id == m.labels:
|
||||||
pureLit = getTempName(m)
|
pureLit = getTempName(m)
|
||||||
cgsym(m, "NimStrPayload")
|
discard cgsym(m, "NimStrPayload")
|
||||||
cgsym(m, "NimStringV2")
|
discard cgsym(m, "NimStringV2")
|
||||||
# string literal not found in the cache:
|
# string literal not found in the cache:
|
||||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||||
else:
|
else:
|
||||||
pureLit = m.tmpBase & rope(id)
|
pureLit = m.tmpBase & rope(id)
|
||||||
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
|
result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit]
|
||||||
|
|
||||||
# ------ Version selector ---------------------------------------------------
|
# ------ Version selector ---------------------------------------------------
|
||||||
|
|
||||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||||
isConst: bool; result: var Rope) =
|
isConst: bool): Rope =
|
||||||
case detectStrVersion(m)
|
case detectStrVersion(m)
|
||||||
of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
|
of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
|
||||||
of 2:
|
of 2:
|
||||||
let tmp = getTempName(m)
|
result = getTempName(m)
|
||||||
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
|
genStringLiteralDataOnlyV2(m, s, result, isConst)
|
||||||
result.add tmp
|
|
||||||
else:
|
else:
|
||||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||||
|
|
||||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
|
||||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
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)
|
case detectStrVersion(m)
|
||||||
of 0, 1: genStringLiteralV1(m, n, result)
|
of 0, 1: result = genStringLiteralV1(m, n)
|
||||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
of 2: result = genStringLiteralV2(m, n, isConst = true)
|
||||||
else:
|
else:
|
||||||
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,13 @@ import
|
||||||
|
|
||||||
const
|
const
|
||||||
CFileSectionNames: array[TCFileSection, string] = [
|
CFileSectionNames: array[TCFileSection, string] = [
|
||||||
|
cfsMergeInfo: "",
|
||||||
cfsHeaders: "NIM_merge_HEADERS",
|
cfsHeaders: "NIM_merge_HEADERS",
|
||||||
cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
|
cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
|
||||||
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
||||||
cfsTypes: "NIM_merge_TYPES",
|
cfsTypes: "NIM_merge_TYPES",
|
||||||
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
||||||
|
cfsFieldInfo: "NIM_merge_FIELD_INFO",
|
||||||
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
||||||
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
||||||
cfsData: "NIM_merge_DATA",
|
cfsData: "NIM_merge_DATA",
|
||||||
|
|
@ -32,8 +34,11 @@ const
|
||||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||||
|
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
|
cfsDebugInit: "NIM_merge_DEBUG_INIT",
|
||||||
|
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
|
||||||
|
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
|
||||||
]
|
]
|
||||||
CProcSectionNames: array[TCProcSection, string] = [
|
CProcSectionNames: array[TCProcSection, string] = [
|
||||||
cpsLocals: "NIM_merge_PROC_LOCALS",
|
cpsLocals: "NIM_merge_PROC_LOCALS",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
||||||
let disc = n[0].sym
|
let disc = n[0].sym
|
||||||
if disc.loc.r == "": fillObjectFields(p.module, typ)
|
if disc.loc.r == nil: fillObjectFields(p.module, typ)
|
||||||
if disc.loc.t == nil:
|
if disc.loc.t == nil:
|
||||||
internalError(p.config, n.info, "specializeResetN()")
|
internalError(p.config, n.info, "specializeResetN()")
|
||||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||||
|
|
@ -42,7 +42,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = n.sym
|
let field = n.sym
|
||||||
if field.typ.kind == tyVoid: return
|
if field.typ.kind == tyVoid: return
|
||||||
if field.loc.r == "": fillObjectFields(p.module, typ)
|
if field.loc.r == nil: fillObjectFields(p.module, typ)
|
||||||
if field.loc.t == nil:
|
if field.loc.t == nil:
|
||||||
internalError(p.config, n.info, "specializeResetN()")
|
internalError(p.config, n.info, "specializeResetN()")
|
||||||
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||||
|
|
@ -87,20 +87,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
||||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||||
of tySet:
|
else:
|
||||||
case mapSetType(p.config, typ)
|
|
||||||
of ctArray:
|
|
||||||
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
|
||||||
[accessor, getTypeDesc(p.module, typ)])
|
|
||||||
of ctInt8, ctInt16, ctInt32, ctInt64:
|
|
||||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
|
||||||
else:
|
|
||||||
doAssert false, "unexpected set type kind"
|
|
||||||
of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
|
||||||
tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
|
|
||||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
|
||||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
|
||||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
|
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc specializeReset(p: BProc, a: TLoc) =
|
proc specializeReset(p: BProc, a: TLoc) =
|
||||||
|
|
|
||||||
|
|
@ -8,44 +8,37 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# included from cgen.nim
|
# included from cgen.nim
|
||||||
|
|
||||||
const
|
const
|
||||||
RangeExpandLimit = 256 # do not generate ranges
|
RangeExpandLimit = 256 # do not generate ranges
|
||||||
# over 'RangeExpandLimit' elements
|
# over 'RangeExpandLimit' elements
|
||||||
stringCaseThreshold = 8
|
stringCaseThreshold = 8
|
||||||
# above X strings a hash-switch for strings is generated
|
# above X strings a hash-switch for strings is generated
|
||||||
|
|
||||||
proc registerTraverseProc(p: BProc, v: PSym) =
|
proc getTraverseProc(p: BProc, v: PSym): Rope =
|
||||||
var traverseProc = ""
|
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, gcRefc} and
|
||||||
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and
|
|
||||||
optOwnedRefs notin p.config.globalOptions and
|
optOwnedRefs notin p.config.globalOptions and
|
||||||
containsGarbageCollectedRef(v.loc.t):
|
containsGarbageCollectedRef(v.loc.t):
|
||||||
# we register a specialized marked proc here; this has the advantage
|
# we register a specialized marked proc here; this has the advantage
|
||||||
# that it works out of the box for thread local storage then :-)
|
# that it works out of the box for thread local storage then :-)
|
||||||
traverseProc = genTraverseProcForGlobal(p.module, v, v.info)
|
result = genTraverseProcForGlobal(p.module, v, v.info)
|
||||||
|
|
||||||
if traverseProc.len != 0 and not p.hcrOn:
|
proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) =
|
||||||
if sfThread in v.flags:
|
if sfThread in v.flags:
|
||||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||||
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
|
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
|
||||||
else:
|
else:
|
||||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||||
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
|
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
|
||||||
|
|
||||||
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
|
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
|
||||||
if n.kind == nkEmpty:
|
if n.kind == nkEmpty: return false
|
||||||
result = false
|
if isInvalidReturnType(conf, n.typ):
|
||||||
elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc:
|
# var v = f()
|
||||||
if isInvalidReturnType(conf, n[0].typ, true):
|
# is transformed into: var v; f(addr v)
|
||||||
# var v = f()
|
# where 'f' **does not** initialize the result!
|
||||||
# is transformed into: var v; f(addr v)
|
return false
|
||||||
# where 'f' **does not** initialize the result!
|
result = true
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
result = true
|
|
||||||
elif isInvalidReturnType(conf, n.typ, false):
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
result = true
|
|
||||||
|
|
||||||
proc inExceptBlockLen(p: BProc): int =
|
proc inExceptBlockLen(p: BProc): int =
|
||||||
for x in p.nestedTryStmts:
|
for x in p.nestedTryStmts:
|
||||||
|
|
@ -54,9 +47,7 @@ proc inExceptBlockLen(p: BProc): int =
|
||||||
proc startBlockInternal(p: BProc): int {.discardable.} =
|
proc startBlockInternal(p: BProc): int {.discardable.} =
|
||||||
inc(p.labels)
|
inc(p.labels)
|
||||||
result = p.blocks.len
|
result = p.blocks.len
|
||||||
|
setLen(p.blocks, result + 1)
|
||||||
p.blocks.add initBlock()
|
|
||||||
|
|
||||||
p.blocks[result].id = p.labels
|
p.blocks[result].id = p.labels
|
||||||
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
||||||
p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
|
p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
|
||||||
|
|
@ -80,7 +71,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||||
|
|
||||||
# check only the first son
|
# check only the first son
|
||||||
var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
|
var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
|
||||||
let hcrCond = if forHcr: getTempName(p.module) else: ""
|
let hcrCond = if forHcr: getTempName(p.module) else: nil
|
||||||
var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]]
|
var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]]
|
||||||
# determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
|
# determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
|
||||||
let isGlobalInBlock = forHcr and p.blocks.len > 2
|
let isGlobalInBlock = forHcr and p.blocks.len > 2
|
||||||
|
|
@ -99,10 +90,13 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||||
let vn = n[i]
|
let vn = n[i]
|
||||||
let v = vn.sym
|
let v = vn.sym
|
||||||
if sfCompileTime in v.flags: continue
|
if sfCompileTime in v.flags: continue
|
||||||
|
var traverseProc: Rope
|
||||||
if sfGlobal in v.flags:
|
if sfGlobal in v.flags:
|
||||||
assignGlobalVar(p, vn, "")
|
assignGlobalVar(p, vn, nil)
|
||||||
genObjectInit(p, cpsInit, v.typ, v.loc, constructObj)
|
genObjectInit(p, cpsInit, v.typ, v.loc, constructObj)
|
||||||
registerTraverseProc(p, v)
|
traverseProc = getTraverseProc(p, v)
|
||||||
|
if traverseProc != nil and not p.hcrOn:
|
||||||
|
registerTraverseProc(p, v, traverseProc)
|
||||||
else:
|
else:
|
||||||
assignLocalVar(p, vn)
|
assignLocalVar(p, vn)
|
||||||
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
|
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
|
||||||
|
|
@ -114,7 +108,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||||
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
|
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
|
||||||
putLocIntoDest(p, v.loc, field)
|
putLocIntoDest(p, v.loc, field)
|
||||||
if forHcr or isGlobalInBlock:
|
if forHcr or isGlobalInBlock:
|
||||||
hcrGlobals.add((loc: v.loc, tp: "NULL"))
|
hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc))
|
||||||
|
|
||||||
if forHcr:
|
if forHcr:
|
||||||
# end the block where the tuple gets initialized
|
# end the block where the tuple gets initialized
|
||||||
|
|
@ -144,12 +138,12 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
|
||||||
a.flags.incl(lfEnforceDeref)
|
a.flags.incl(lfEnforceDeref)
|
||||||
expr(p, ri, a)
|
expr(p, ri, a)
|
||||||
|
|
||||||
proc assignLabel(b: var TBlock; result: var Rope) {.inline.} =
|
proc assignLabel(b: var TBlock): Rope {.inline.} =
|
||||||
b.label = "LA" & b.id.rope
|
b.label = "LA" & b.id.rope
|
||||||
result.add b.label
|
result = b.label
|
||||||
|
|
||||||
proc blockBody(b: var TBlock; result: var Rope) =
|
proc blockBody(b: var TBlock): Rope =
|
||||||
result.add b.sections[cpsLocals]
|
result = b.sections[cpsLocals]
|
||||||
if b.frameLen > 0:
|
if b.frameLen > 0:
|
||||||
result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
|
result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
|
||||||
result.add(b.sections[cpsInit])
|
result.add(b.sections[cpsInit])
|
||||||
|
|
@ -158,7 +152,7 @@ proc blockBody(b: var TBlock; result: var Rope) =
|
||||||
proc endBlock(p: BProc, blockEnd: Rope) =
|
proc endBlock(p: BProc, blockEnd: Rope) =
|
||||||
let topBlock = p.blocks.len-1
|
let topBlock = p.blocks.len-1
|
||||||
# the block is merged into the parent block
|
# the block is merged into the parent block
|
||||||
p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts])
|
p.blocks[topBlock-1].sections[cpsStmts].add(p.blocks[topBlock].blockBody)
|
||||||
setLen(p.blocks, topBlock)
|
setLen(p.blocks, topBlock)
|
||||||
# this is done after the block is popped so $n is
|
# this is done after the block is popped so $n is
|
||||||
# properly indented when pretty printing is enabled
|
# properly indented when pretty printing is enabled
|
||||||
|
|
@ -170,7 +164,7 @@ proc endBlock(p: BProc) =
|
||||||
var blockEnd: Rope
|
var blockEnd: Rope
|
||||||
if frameLen > 0:
|
if frameLen > 0:
|
||||||
blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
|
blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
|
||||||
if p.blocks[topBlock].label.len != 0:
|
if p.blocks[topBlock].label != nil:
|
||||||
blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label])
|
blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label])
|
||||||
else:
|
else:
|
||||||
blockEnd.addf("}$n", [])
|
blockEnd.addf("}$n", [])
|
||||||
|
|
@ -278,15 +272,17 @@ proc genGotoVar(p: BProc; value: PNode) =
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||||
|
|
||||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
|
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope
|
||||||
|
|
||||||
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
|
proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope =
|
||||||
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
|
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
|
||||||
discard "nothing to do"
|
result = nil
|
||||||
elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
|
elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
|
||||||
p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
|
p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
|
||||||
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
|
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
|
||||||
genBracedInit(p, value, isConst = false, v.typ, result)
|
result = genBracedInit(p, value, isConst = false, v.typ)
|
||||||
|
else:
|
||||||
|
result = nil
|
||||||
|
|
||||||
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
if sfGoto in v.flags:
|
if sfGoto in v.flags:
|
||||||
|
|
@ -294,8 +290,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
genGotoVar(p, value)
|
genGotoVar(p, value)
|
||||||
return
|
return
|
||||||
var targetProc = p
|
var targetProc = p
|
||||||
var valueAsRope = ""
|
var traverseProc: Rope
|
||||||
potentialValueInit(p, v, value, valueAsRope)
|
let valueAsRope = potentialValueInit(p, v, value)
|
||||||
if sfGlobal in v.flags:
|
if sfGlobal in v.flags:
|
||||||
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
||||||
value.kind == nkEmpty and
|
value.kind == nkEmpty and
|
||||||
|
|
@ -311,7 +307,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
# That's why we are doing the construction inside the preInitProc.
|
# That's why we are doing the construction inside the preInitProc.
|
||||||
# genObjectInit relies on the C runtime's guarantees that
|
# genObjectInit relies on the C runtime's guarantees that
|
||||||
# global variables will be initialized to zero.
|
# global variables will be initialized to zero.
|
||||||
if valueAsRope.len == 0:
|
if valueAsRope == nil:
|
||||||
var loc = v.loc
|
var loc = v.loc
|
||||||
|
|
||||||
# When the native TLS is unavailable, a global thread-local variable needs
|
# When the native TLS is unavailable, a global thread-local variable needs
|
||||||
|
|
@ -325,7 +321,9 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
|
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
|
||||||
if sfExportc in v.flags and p.module.g.generatedHeader != nil:
|
if sfExportc in v.flags and p.module.g.generatedHeader != nil:
|
||||||
genVarPrototype(p.module.g.generatedHeader, vn)
|
genVarPrototype(p.module.g.generatedHeader, vn)
|
||||||
registerTraverseProc(p, v)
|
traverseProc = getTraverseProc(p, v)
|
||||||
|
if traverseProc != nil and not p.hcrOn:
|
||||||
|
registerTraverseProc(p, v, traverseProc)
|
||||||
else:
|
else:
|
||||||
let imm = isAssignedImmediately(p.config, value)
|
let imm = isAssignedImmediately(p.config, value)
|
||||||
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
||||||
|
|
@ -338,14 +336,14 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
var tmp: TLoc
|
var tmp: TLoc
|
||||||
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||||
sfConstructor in value[0].sym.flags:
|
sfConstructor in value[0].sym.flags:
|
||||||
var params = newRopeAppender()
|
var params: Rope
|
||||||
var argsCounter = 0
|
|
||||||
let typ = skipTypes(value[0].typ, abstractInst)
|
let typ = skipTypes(value[0].typ, abstractInst)
|
||||||
assert(typ.kind == tyProc)
|
assert(typ.kind == tyProc)
|
||||||
for i in 1..<value.len:
|
for i in 1..<value.len:
|
||||||
|
if params != nil: params.add(~", ")
|
||||||
assert(typ.len == typ.n.len)
|
assert(typ.len == typ.n.len)
|
||||||
genOtherArg(p, value, i, typ, params, argsCounter)
|
params.add(genOtherArg(p, value, i, typ))
|
||||||
if params.len == 0:
|
if params == nil:
|
||||||
lineF(p, cpsStmts, "$#;$n", [decl])
|
lineF(p, cpsStmts, "$#;$n", [decl])
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
|
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
|
||||||
|
|
@ -356,7 +354,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
assignLocalVar(p, vn)
|
assignLocalVar(p, vn)
|
||||||
initLocalVar(p, v, imm)
|
initLocalVar(p, v, imm)
|
||||||
|
|
||||||
let traverseProc = "NULL"
|
if traverseProc == nil: traverseProc = ~"NULL"
|
||||||
# If the var is in a block (control flow like if/while or a block) in global scope just
|
# If the var is in a block (control flow like if/while or a block) in global scope just
|
||||||
# register the so called "global" so it can be used later on. There is no need to close
|
# register the so called "global" so it can be used later on. There is no need to close
|
||||||
# and reopen of if (nim_hcr_do_init_) blocks because we are in one already anyway.
|
# and reopen of if (nim_hcr_do_init_) blocks because we are in one already anyway.
|
||||||
|
|
@ -376,7 +374,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||||
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
|
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
|
||||||
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
|
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
|
||||||
startBlock(targetProc)
|
startBlock(targetProc)
|
||||||
if value.kind != nkEmpty and valueAsRope.len == 0:
|
if value.kind != nkEmpty and valueAsRope == nil:
|
||||||
genLineDir(targetProc, vn)
|
genLineDir(targetProc, vn)
|
||||||
loadInto(targetProc, vn, value, v.loc)
|
loadInto(targetProc, vn, value, v.loc)
|
||||||
if forHcr:
|
if forHcr:
|
||||||
|
|
@ -400,7 +398,7 @@ proc genClosureVar(p: BProc, a: PNode) =
|
||||||
genLineDir(p, a)
|
genLineDir(p, a)
|
||||||
if immediateAsgn:
|
if immediateAsgn:
|
||||||
loadInto(p, a[0], a[2], v)
|
loadInto(p, a[0], a[2], v)
|
||||||
elif sfNoInit notin a[0][1].sym.flags:
|
else:
|
||||||
constructLoc(p, v)
|
constructLoc(p, v)
|
||||||
|
|
||||||
proc genVarStmt(p: BProc, n: PNode) =
|
proc genVarStmt(p: BProc, n: PNode) =
|
||||||
|
|
@ -555,9 +553,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
|
||||||
return
|
return
|
||||||
|
|
||||||
let val = getOrdValue(it[j])
|
let val = getOrdValue(it[j])
|
||||||
var lit = newRopeAppender()
|
lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(toInt64(val)+id+1)])
|
||||||
intLiteral(toInt64(val)+id+1, lit)
|
|
||||||
lineF(p, cpsStmts, "TMP$#_:$n", [lit])
|
|
||||||
|
|
||||||
genStmts(p, it.lastSon)
|
genStmts(p, it.lastSon)
|
||||||
|
|
||||||
|
|
@ -611,9 +607,8 @@ proc genWhileStmt(p: BProc, t: PNode) =
|
||||||
p.blocks[p.breakIdx].isLoop = true
|
p.blocks[p.breakIdx].isLoop = true
|
||||||
initLocExpr(p, t[0], a)
|
initLocExpr(p, t[0], a)
|
||||||
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
|
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
|
||||||
lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
|
let label = assignLabel(p.blocks[p.breakIdx])
|
||||||
assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
|
lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
|
||||||
lineF(p, cpsStmts, ";$n", [])
|
|
||||||
genStmts(p, loopBody)
|
genStmts(p, loopBody)
|
||||||
|
|
||||||
if optProfiler in p.options:
|
if optProfiler in p.options:
|
||||||
|
|
@ -700,12 +695,12 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
||||||
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
|
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
|
||||||
if idx < 0 or not p.blocks[idx].isLoop:
|
if idx < 0 or not p.blocks[idx].isLoop:
|
||||||
internalError(p.config, t.info, "no loop to break")
|
internalError(p.config, t.info, "no loop to break")
|
||||||
p.blocks[idx].label = "LA" & p.blocks[idx].id.rope
|
let label = assignLabel(p.blocks[idx])
|
||||||
blockLeaveActions(p,
|
blockLeaveActions(p,
|
||||||
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
|
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
|
||||||
p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
|
p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label])
|
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||||
|
|
||||||
proc raiseExit(p: BProc) =
|
proc raiseExit(p: BProc) =
|
||||||
assert p.config.exc == excGoto
|
assert p.config.exc == excGoto
|
||||||
|
|
@ -727,19 +722,21 @@ proc finallyActions(p: BProc) =
|
||||||
if finallyBlock != nil:
|
if finallyBlock != nil:
|
||||||
genSimpleBlock(p, finallyBlock[0])
|
genSimpleBlock(p, finallyBlock[0])
|
||||||
|
|
||||||
proc raiseInstr(p: BProc; result: var Rope) =
|
proc raiseInstr(p: BProc): Rope =
|
||||||
if p.config.exc == excGoto:
|
if p.config.exc == excGoto:
|
||||||
let L = p.nestedTryStmts.len
|
let L = p.nestedTryStmts.len
|
||||||
if L == 0:
|
if L == 0:
|
||||||
p.flags.incl beforeRetNeeded
|
p.flags.incl beforeRetNeeded
|
||||||
# easy case, simply goto 'ret':
|
# easy case, simply goto 'ret':
|
||||||
result.add ropecg(p.module, "goto BeforeRet_;$n", [])
|
result = ropecg(p.module, "goto BeforeRet_;$n", [])
|
||||||
else:
|
else:
|
||||||
# raise inside an 'except' must go to the finally block,
|
# raise inside an 'except' must go to the finally block,
|
||||||
# raise outside an 'except' block must go to the 'except' list.
|
# raise outside an 'except' block must go to the 'except' list.
|
||||||
result.add ropecg(p.module, "goto LA$1_;$n",
|
result = ropecg(p.module, "goto LA$1_;$n",
|
||||||
[p.nestedTryStmts[L-1].label])
|
[p.nestedTryStmts[L-1].label])
|
||||||
# + ord(p.nestedTryStmts[L-1].inExcept)])
|
# + ord(p.nestedTryStmts[L-1].inExcept)])
|
||||||
|
else:
|
||||||
|
result = nil
|
||||||
|
|
||||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
if t[0].kind != nkEmpty:
|
if t[0].kind != nkEmpty:
|
||||||
|
|
@ -768,10 +765,12 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
# reraise the last exception:
|
# reraise the last exception:
|
||||||
if p.config.exc == excCpp:
|
if p.config.exc == excCpp:
|
||||||
line(p, cpsStmts, "throw;\n")
|
line(p, cpsStmts, ~"throw;$n")
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||||
raiseInstr(p, p.s(cpsStmts))
|
let gotoInstr = raiseInstr(p)
|
||||||
|
if gotoInstr != nil:
|
||||||
|
line(p, cpsStmts, gotoInstr)
|
||||||
|
|
||||||
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||||
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
||||||
|
|
@ -831,27 +830,17 @@ template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
|
||||||
fixLabel(p, lend)
|
fixLabel(p, lend)
|
||||||
|
|
||||||
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
|
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
|
||||||
stringKind: TTypeKind,
|
|
||||||
branches: var openArray[Rope]) =
|
branches: var openArray[Rope]) =
|
||||||
var x: TLoc
|
var x: TLoc
|
||||||
for i in 0..<b.len - 1:
|
for i in 0..<b.len - 1:
|
||||||
assert(b[i].kind != nkRange)
|
assert(b[i].kind != nkRange)
|
||||||
initLocExpr(p, b[i], x)
|
initLocExpr(p, b[i], x)
|
||||||
var j: int
|
assert(b[i].kind in {nkStrLit..nkTripleStrLit})
|
||||||
case b[i].kind
|
var j = int(hashString(p.config, b[i].strVal) and high(branches))
|
||||||
of nkStrLit..nkTripleStrLit:
|
appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
|
||||||
j = int(hashString(p.config, b[i].strVal) and high(branches))
|
|
||||||
of nkNilLit: j = 0
|
|
||||||
else:
|
|
||||||
assert false, "invalid string case branch node kind"
|
|
||||||
if stringKind == tyCstring:
|
|
||||||
appcg(p.module, branches[j], "if (#eqCstrings($1, $2)) goto $3;$n",
|
|
||||||
[rdLoc(e), rdLoc(x), labl])
|
|
||||||
else:
|
|
||||||
appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
|
|
||||||
[rdLoc(e), rdLoc(x), labl])
|
[rdLoc(e), rdLoc(x), labl])
|
||||||
|
|
||||||
proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
|
proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
|
||||||
# count how many constant strings there are in the case:
|
# count how many constant strings there are in the case:
|
||||||
var strings = 0
|
var strings = 0
|
||||||
for i in 1..<t.len:
|
for i in 1..<t.len:
|
||||||
|
|
@ -867,23 +856,17 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
|
||||||
inc(p.labels)
|
inc(p.labels)
|
||||||
if t[i].kind == nkOfBranch:
|
if t[i].kind == nkOfBranch:
|
||||||
genCaseStringBranch(p, t[i], a, "LA" & rope(p.labels) & "_",
|
genCaseStringBranch(p, t[i], a, "LA" & rope(p.labels) & "_",
|
||||||
stringKind, branches)
|
branches)
|
||||||
else:
|
else:
|
||||||
# else statement: nothing to do yet
|
# else statement: nothing to do yet
|
||||||
# but we reserved a label, which we use later
|
# but we reserved a label, which we use later
|
||||||
discard
|
discard
|
||||||
if stringKind == tyCstring:
|
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
|
||||||
linefmt(p, cpsStmts, "switch (#hashCstring($1) & $2) {$n",
|
[rdLoc(a), bitMask])
|
||||||
[rdLoc(a), bitMask])
|
|
||||||
else:
|
|
||||||
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
|
|
||||||
[rdLoc(a), bitMask])
|
|
||||||
for j in 0..high(branches):
|
for j in 0..high(branches):
|
||||||
if branches[j] != "":
|
if branches[j] != nil:
|
||||||
var lit = newRopeAppender()
|
|
||||||
intLiteral(j, lit)
|
|
||||||
lineF(p, cpsStmts, "case $1: $n$2break;$n",
|
lineF(p, cpsStmts, "case $1: $n$2break;$n",
|
||||||
[lit, branches[j]])
|
[intLiteral(j), branches[j]])
|
||||||
lineF(p, cpsStmts, "}$n", []) # else statement:
|
lineF(p, cpsStmts, "}$n", []) # else statement:
|
||||||
if t[^1].kind != nkOfBranch:
|
if t[^1].kind != nkOfBranch:
|
||||||
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
|
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
|
||||||
|
|
@ -891,10 +874,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
|
||||||
var lend = genCaseSecondPass(p, t, d, labId, t.len-1)
|
var lend = genCaseSecondPass(p, t, d, labId, t.len-1)
|
||||||
fixLabel(p, lend)
|
fixLabel(p, lend)
|
||||||
else:
|
else:
|
||||||
if stringKind == tyCstring:
|
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
|
||||||
genCaseGeneric(p, t, d, "", "if (#eqCstrings($1, $2)) goto $3;$n")
|
|
||||||
else:
|
|
||||||
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
|
|
||||||
|
|
||||||
proc branchHasTooBigRange(b: PNode): bool =
|
proc branchHasTooBigRange(b: PNode): bool =
|
||||||
for it in b:
|
for it in b:
|
||||||
|
|
@ -917,22 +897,16 @@ proc genCaseRange(p: BProc, branch: PNode) =
|
||||||
for j in 0..<branch.len-1:
|
for j in 0..<branch.len-1:
|
||||||
if branch[j].kind == nkRange:
|
if branch[j].kind == nkRange:
|
||||||
if hasSwitchRange in CC[p.config.cCompiler].props:
|
if hasSwitchRange in CC[p.config.cCompiler].props:
|
||||||
var litA = newRopeAppender()
|
lineF(p, cpsStmts, "case $1 ... $2:$n", [
|
||||||
var litB = newRopeAppender()
|
genLiteral(p, branch[j][0]),
|
||||||
genLiteral(p, branch[j][0], litA)
|
genLiteral(p, branch[j][1])])
|
||||||
genLiteral(p, branch[j][1], litB)
|
|
||||||
lineF(p, cpsStmts, "case $1 ... $2:$n", [litA, litB])
|
|
||||||
else:
|
else:
|
||||||
var v = copyNode(branch[j][0])
|
var v = copyNode(branch[j][0])
|
||||||
while v.intVal <= branch[j][1].intVal:
|
while v.intVal <= branch[j][1].intVal:
|
||||||
var litA = newRopeAppender()
|
lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, v)])
|
||||||
genLiteral(p, v, litA)
|
|
||||||
lineF(p, cpsStmts, "case $1:$n", [litA])
|
|
||||||
inc(v.intVal)
|
inc(v.intVal)
|
||||||
else:
|
else:
|
||||||
var litA = newRopeAppender()
|
lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])])
|
||||||
genLiteral(p, branch[j], litA)
|
|
||||||
lineF(p, cpsStmts, "case $1:$n", [litA])
|
|
||||||
|
|
||||||
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||||
# analyse 'case' statement:
|
# analyse 'case' statement:
|
||||||
|
|
@ -944,7 +918,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||||
var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
|
var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
|
||||||
rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
||||||
eqFormat = "if ($1 == $2) goto $3;$n",
|
eqFormat = "if ($1 == $2) goto $3;$n",
|
||||||
splitPoint, a) else: ""
|
splitPoint, a) else: nil
|
||||||
|
|
||||||
# generate switch part (might be empty):
|
# generate switch part (might be empty):
|
||||||
if splitPoint+1 < n.len:
|
if splitPoint+1 < n.len:
|
||||||
|
|
@ -965,7 +939,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||||
if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
|
if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
|
||||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||||
lineF(p, cpsStmts, "}$n", [])
|
lineF(p, cpsStmts, "}$n", [])
|
||||||
if lend != "": fixLabel(p, lend)
|
if lend != nil: fixLabel(p, lend)
|
||||||
|
|
||||||
proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
|
|
@ -973,9 +947,7 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||||
getTemp(p, t.typ, d)
|
getTemp(p, t.typ, d)
|
||||||
case skipTypes(t[0].typ, abstractVarRange).kind
|
case skipTypes(t[0].typ, abstractVarRange).kind
|
||||||
of tyString:
|
of tyString:
|
||||||
genStringCase(p, t, tyString, d)
|
genStringCase(p, t, d)
|
||||||
of tyCstring:
|
|
||||||
genStringCase(p, t, tyCstring, d)
|
|
||||||
of tyFloat..tyFloat128:
|
of tyFloat..tyFloat128:
|
||||||
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
||||||
"if ($1 == $2) goto $3;$n")
|
"if ($1 == $2) goto $3;$n")
|
||||||
|
|
@ -1066,7 +1038,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
else:
|
else:
|
||||||
var orExpr = newRopeAppender()
|
var orExpr = Rope(nil)
|
||||||
var exvar = PNode(nil)
|
var exvar = PNode(nil)
|
||||||
for j in 0..<t[i].len - 1:
|
for j in 0..<t[i].len - 1:
|
||||||
var typeNode = t[i][j]
|
var typeNode = t[i][j]
|
||||||
|
|
@ -1077,24 +1049,22 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
if isImportedException(typeNode.typ, p.config):
|
if isImportedException(typeNode.typ, p.config):
|
||||||
hasImportedCppExceptions = true
|
hasImportedCppExceptions = true
|
||||||
else:
|
else:
|
||||||
if orExpr.len != 0: orExpr.add("||")
|
if orExpr != nil: orExpr.add("||")
|
||||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||||
if optTinyRtti in p.config.globalOptions:
|
genTypeInfo2Name(p.module, typeNode.typ)
|
||||||
let checkFor = $getObjDepth(typeNode.typ)
|
|
||||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))])
|
|
||||||
else:
|
else:
|
||||||
let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
|
genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
|
||||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
if orExpr.len != 0:
|
if orExpr != nil:
|
||||||
if hasIf:
|
if hasIf:
|
||||||
startBlock(p, "else if ($1) {$n", [orExpr])
|
startBlock(p, "else if ($1) {$n", [orExpr])
|
||||||
else:
|
else:
|
||||||
startBlock(p, "if ($1) {$n", [orExpr])
|
startBlock(p, "if ($1) {$n", [orExpr])
|
||||||
hasIf = true
|
hasIf = true
|
||||||
if exvar != nil:
|
if exvar != nil:
|
||||||
fillLocalName(p, exvar.sym)
|
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack)
|
||||||
fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
|
|
||||||
linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
|
linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
|
||||||
rdLoc(exvar.sym.loc), rope(etmp+1)])
|
rdLoc(exvar.sym.loc), rope(etmp+1)])
|
||||||
# we handled the error:
|
# we handled the error:
|
||||||
|
|
@ -1135,8 +1105,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
typeNode = t[i][j][1]
|
typeNode = t[i][j][1]
|
||||||
if isImportedException(typeNode.typ, p.config):
|
if isImportedException(typeNode.typ, p.config):
|
||||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||||
fillLocalName(p, exvar.sym)
|
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack)
|
||||||
fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
|
|
||||||
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc))
|
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc))
|
||||||
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
|
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
|
|
@ -1189,7 +1158,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||||
if not isEmptyType(t.typ) and d.k == locNone:
|
if not isEmptyType(t.typ) and d.k == locNone:
|
||||||
getTemp(p, t.typ, d)
|
getTemp(p, t.typ, d)
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
cgsym(p.module, "popCurrentExceptionEx")
|
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||||
startBlock(p, "try {$n")
|
startBlock(p, "try {$n")
|
||||||
|
|
@ -1215,8 +1184,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||||
for j in 0..<t[i].len-1:
|
for j in 0..<t[i].len-1:
|
||||||
if t[i][j].isInfixAs():
|
if t[i][j].isInfixAs():
|
||||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||||
fillLocalName(p, exvar.sym)
|
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
|
||||||
fillLoc(exvar.sym.loc, locTemp, exvar, OnUnknown)
|
|
||||||
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
|
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
|
||||||
else:
|
else:
|
||||||
startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
|
startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
|
||||||
|
|
@ -1231,7 +1199,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||||
# finally requires catch all presence
|
# finally requires catch all presence
|
||||||
startBlock(p, "catch (...) {$n")
|
startBlock(p, "catch (...) {$n")
|
||||||
genStmts(p, t[^1][0])
|
genStmts(p, t[^1][0])
|
||||||
line(p, cpsStmts, "throw;\n")
|
line(p, cpsStmts, ~"throw;$n")
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
|
|
||||||
genSimpleBlock(p, t[^1][0])
|
genSimpleBlock(p, t[^1][0])
|
||||||
|
|
@ -1293,17 +1261,16 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
|
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
|
||||||
expr(p, t[i][0], d)
|
expr(p, t[i][0], d)
|
||||||
else:
|
else:
|
||||||
var orExpr = newRopeAppender()
|
var orExpr: Rope = nil
|
||||||
for j in 0..<t[i].len - 1:
|
for j in 0..<t[i].len - 1:
|
||||||
assert(t[i][j].kind == nkType)
|
assert(t[i][j].kind == nkType)
|
||||||
if orExpr.len != 0: orExpr.add("||")
|
if orExpr != nil: orExpr.add("||")
|
||||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||||
if optTinyRtti in p.config.globalOptions:
|
genTypeInfo2Name(p.module, t[i][j].typ)
|
||||||
let checkFor = $getObjDepth(t[i][j].typ)
|
|
||||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
|
|
||||||
else:
|
else:
|
||||||
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
if i > 1: line(p, cpsStmts, "else ")
|
if i > 1: line(p, cpsStmts, "else ")
|
||||||
startBlock(p, "if ($1) {$n", [orExpr])
|
startBlock(p, "if ($1) {$n", [orExpr])
|
||||||
|
|
@ -1379,7 +1346,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
p.flags.incl noSafePoints
|
p.flags.incl noSafePoints
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
cgsym(p.module, "Exception")
|
discard cgsym(p.module, "Exception")
|
||||||
var safePoint: Rope
|
var safePoint: Rope
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
safePoint = getTempName(p.module)
|
safePoint = getTempName(p.module)
|
||||||
|
|
@ -1389,24 +1356,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||||
elif isDefined(p.config, "nimSigSetjmp"):
|
elif isDefined(p.config, "nimSigSetjmp"):
|
||||||
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint])
|
||||||
elif isDefined(p.config, "nimBuiltinSetjmp"):
|
|
||||||
linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint])
|
|
||||||
elif isDefined(p.config, "nimRawSetjmp"):
|
elif isDefined(p.config, "nimRawSetjmp"):
|
||||||
if isDefined(p.config, "mswindows"):
|
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
||||||
if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"):
|
|
||||||
# For the vcc compiler, use `setjmp()` with one argument.
|
|
||||||
# See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170
|
|
||||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
|
||||||
else:
|
|
||||||
# The Windows `_setjmp()` takes two arguments, with the second being an
|
|
||||||
# undocumented buffer used by the SEH mechanism for stack unwinding.
|
|
||||||
# Mingw-w64 has been trying to get it right for years, but it's still
|
|
||||||
# prone to stack corruption during unwinding, so we disable that by setting
|
|
||||||
# it to NULL.
|
|
||||||
# More details: https://github.com/status-im/nimbus-eth2/issues/3121
|
|
||||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint])
|
|
||||||
else:
|
|
||||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||||
lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
|
lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
|
||||||
|
|
@ -1438,17 +1389,16 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||||
endBlock(p)
|
endBlock(p)
|
||||||
else:
|
else:
|
||||||
var orExpr = newRopeAppender()
|
var orExpr: Rope = nil
|
||||||
for j in 0..<t[i].len - 1:
|
for j in 0..<t[i].len - 1:
|
||||||
assert(t[i][j].kind == nkType)
|
assert(t[i][j].kind == nkType)
|
||||||
if orExpr.len != 0: orExpr.add("||")
|
if orExpr != nil: orExpr.add("||")
|
||||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||||
if optTinyRtti in p.config.globalOptions:
|
genTypeInfo2Name(p.module, t[i][j].typ)
|
||||||
let checkFor = $getObjDepth(t[i][j].typ)
|
|
||||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
|
|
||||||
else:
|
else:
|
||||||
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||||
|
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||||
|
|
||||||
if i > 1: line(p, cpsStmts, "else ")
|
if i > 1: line(p, cpsStmts, "else ")
|
||||||
startBlock(p, "if ($1) {$n", [orExpr])
|
startBlock(p, "if ($1) {$n", [orExpr])
|
||||||
|
|
@ -1474,7 +1424,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
|
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
|
||||||
|
|
||||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
|
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
|
||||||
var res = ""
|
var res = ""
|
||||||
for it in t.sons:
|
for it in t.sons:
|
||||||
case it.kind
|
case it.kind
|
||||||
|
|
@ -1490,8 +1440,13 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
|
||||||
res.add($getTypeDesc(p.module, sym.typ))
|
res.add($getTypeDesc(p.module, sym.typ))
|
||||||
else:
|
else:
|
||||||
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
|
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
|
||||||
fillBackendName(p.module, sym)
|
var r = sym.loc.r
|
||||||
res.add($sym.loc.r)
|
if r == nil:
|
||||||
|
# if no name has already been given,
|
||||||
|
# it doesn't matter much:
|
||||||
|
r = mangleName(p.module, sym)
|
||||||
|
sym.loc.r = r # but be consequent!
|
||||||
|
res.add($r)
|
||||||
of nkTypeOfExpr:
|
of nkTypeOfExpr:
|
||||||
res.add($getTypeDesc(p.module, it.typ))
|
res.add($getTypeDesc(p.module, it.typ))
|
||||||
else:
|
else:
|
||||||
|
|
@ -1516,13 +1471,12 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
|
||||||
result.add("\\n\"\n")
|
result.add("\\n\"\n")
|
||||||
else:
|
else:
|
||||||
res.add("\L")
|
res.add("\L")
|
||||||
result.add res.rope
|
result = res.rope
|
||||||
|
|
||||||
proc genAsmStmt(p: BProc, t: PNode) =
|
proc genAsmStmt(p: BProc, t: PNode) =
|
||||||
assert(t.kind == nkAsmStmt)
|
assert(t.kind == nkAsmStmt)
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
var s = newRopeAppender()
|
var s = genAsmOrEmitStmt(p, t, isAsmStmt=true)
|
||||||
genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
|
|
||||||
# see bug #2362, "top level asm statements" seem to be a mis-feature
|
# see bug #2362, "top level asm statements" seem to be a mis-feature
|
||||||
# but even if we don't do this, the example in #2362 cannot possibly
|
# but even if we don't do this, the example in #2362 cannot possibly
|
||||||
# work:
|
# work:
|
||||||
|
|
@ -1530,20 +1484,18 @@ proc genAsmStmt(p: BProc, t: PNode) =
|
||||||
# top level asm statement?
|
# top level asm statement?
|
||||||
p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
|
p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
|
||||||
else:
|
else:
|
||||||
addIndent p, p.s(cpsStmts)
|
p.s(cpsStmts).add indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]))
|
||||||
p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
|
|
||||||
|
|
||||||
proc determineSection(n: PNode): TCFileSection =
|
proc determineSection(n: PNode): TCFileSection =
|
||||||
result = cfsProcHeaders
|
result = cfsProcHeaders
|
||||||
if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}:
|
if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
let sec = n[0].strVal
|
let sec = n[0].strVal
|
||||||
if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND
|
if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes
|
||||||
elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
|
elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
|
||||||
elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
|
elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
|
||||||
|
|
||||||
proc genEmit(p: BProc, t: PNode) =
|
proc genEmit(p: BProc, t: PNode) =
|
||||||
var s = newRopeAppender()
|
var s = genAsmOrEmitStmt(p, t[1])
|
||||||
genAsmOrEmitStmt(p, t[1], false, s)
|
|
||||||
if p.prc == nil:
|
if p.prc == nil:
|
||||||
# top level emit pragma?
|
# top level emit pragma?
|
||||||
let section = determineSection(t[1])
|
let section = determineSection(t[1])
|
||||||
|
|
@ -1554,14 +1506,14 @@ proc genEmit(p: BProc, t: PNode) =
|
||||||
line(p, cpsStmts, s)
|
line(p, cpsStmts, s)
|
||||||
|
|
||||||
proc genPragma(p: BProc, n: PNode) =
|
proc genPragma(p: BProc, n: PNode) =
|
||||||
for i in 0..<n.len:
|
for it in n.sons:
|
||||||
let it = n[i]
|
|
||||||
case whichPragma(it)
|
case whichPragma(it)
|
||||||
of wEmit: genEmit(p, it)
|
of wEmit: genEmit(p, it)
|
||||||
of wPush:
|
of wInjectStmt:
|
||||||
processPushBackendOption(p.optionsStack, p.options, n, i+1)
|
var p = newProc(nil, p.module)
|
||||||
of wPop:
|
p.options.excl {optLineTrace, optStackTrace}
|
||||||
processPopBackendOption(p.optionsStack, p.options)
|
genStmts(p, it[1])
|
||||||
|
p.module.injectStmt = p.s(cpsStmts)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1573,12 +1525,10 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
|
||||||
if not containsOrIncl(p.module.declaredThings, field.id):
|
if not containsOrIncl(p.module.declaredThings, field.id):
|
||||||
appcg(p.module, cfsVars, "extern $1",
|
appcg(p.module, cfsVars, "extern $1",
|
||||||
[discriminatorTableDecl(p.module, t, field)])
|
[discriminatorTableDecl(p.module, t, field)])
|
||||||
var lit = newRopeAppender()
|
|
||||||
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1, lit)
|
|
||||||
lineCg(p, cpsStmts,
|
lineCg(p, cpsStmts,
|
||||||
"#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
|
"#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
|
||||||
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
|
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
|
||||||
lit])
|
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)])
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
|
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
|
||||||
|
|
@ -1603,7 +1553,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||||
initLocExpr(p, e[0], a)
|
initLocExpr(p, e[0], a)
|
||||||
getTemp(p, a.t, tmp)
|
getTemp(p, a.t, tmp)
|
||||||
expr(p, e[1], tmp)
|
expr(p, e[1], tmp)
|
||||||
if optTinyRtti notin p.config.globalOptions and p.inUncheckedAssignSection == 0:
|
if optTinyRtti notin p.config.globalOptions:
|
||||||
let field = dotExpr[1].sym
|
let field = dotExpr[1].sym
|
||||||
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
|
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
|
||||||
message(p.config, e.info, warnCaseTransition)
|
message(p.config, e.info, warnCaseTransition)
|
||||||
|
|
@ -1624,7 +1574,6 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||||
initLoc(a, locNone, le, OnUnknown)
|
initLoc(a, locNone, le, OnUnknown)
|
||||||
a.flags.incl(lfEnforceDeref)
|
a.flags.incl(lfEnforceDeref)
|
||||||
a.flags.incl(lfPrepareForMutation)
|
a.flags.incl(lfPrepareForMutation)
|
||||||
genLineDir(p, le) # it can be a nkBracketExpr, which may raise
|
|
||||||
expr(p, le, a)
|
expr(p, le, a)
|
||||||
a.flags.excl(lfPrepareForMutation)
|
a.flags.excl(lfPrepareForMutation)
|
||||||
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
||||||
|
|
|
||||||
|
|
@ -44,13 +44,13 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||||
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
|
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
|
||||||
|
|
||||||
proc generateThreadLocalStorage(m: BModule) =
|
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)
|
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
||||||
finishTypeDescriptions(m)
|
finishTypeDescriptions(m)
|
||||||
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||||
|
|
||||||
proc generateThreadVarsSize(m: BModule) =
|
proc generateThreadVarsSize(m: BModule) =
|
||||||
if m.g.nimtv != "":
|
if m.g.nimtv != nil:
|
||||||
let externc = if m.config.backend == backendCpp or
|
let externc = if m.config.backend == backendCpp or
|
||||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
||||||
else: ""
|
else: ""
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||||
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
||||||
var p = c.p
|
var p = c.p
|
||||||
let disc = n[0].sym
|
let disc = n[0].sym
|
||||||
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
|
if disc.loc.r == nil: fillObjectFields(c.p.module, typ)
|
||||||
if disc.loc.t == nil:
|
if disc.loc.t == nil:
|
||||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||||
|
|
@ -51,7 +51,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = n.sym
|
let field = n.sym
|
||||||
if field.typ.kind == tyVoid: return
|
if field.typ.kind == tyVoid: return
|
||||||
if field.loc.r == "": fillObjectFields(c.p.module, typ)
|
if field.loc.r == nil: fillObjectFields(c.p.module, typ)
|
||||||
if field.loc.t == nil:
|
if field.loc.t == nil:
|
||||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||||
|
|
@ -76,8 +76,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||||
let arraySize = lengthOrd(c.p.config, typ[0])
|
let arraySize = lengthOrd(c.p.config, typ[0])
|
||||||
var i: TLoc
|
var i: TLoc
|
||||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
||||||
var oldCode = p.s(cpsStmts)
|
let oldCode = p.s(cpsStmts)
|
||||||
freeze oldCode
|
|
||||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||||
[i.r, arraySize])
|
[i.r, arraySize])
|
||||||
let oldLen = p.s(cpsStmts).len
|
let oldLen = p.s(cpsStmts).len
|
||||||
|
|
@ -121,8 +120,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||||
assert typ.kind == tySequence
|
assert typ.kind == tySequence
|
||||||
var i: TLoc
|
var i: TLoc
|
||||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
||||||
var oldCode = p.s(cpsStmts)
|
let oldCode = p.s(cpsStmts)
|
||||||
freeze oldCode
|
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
a.r = accessor
|
a.r = accessor
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@
|
||||||
# ------------------------- Name Mangling --------------------------------
|
# ------------------------- Name Mangling --------------------------------
|
||||||
|
|
||||||
import sighashes, modulegraphs
|
import sighashes, modulegraphs
|
||||||
import std/md5
|
from lowerings import createObj
|
||||||
|
|
||||||
|
proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope
|
||||||
|
|
||||||
proc isKeyword(w: PIdent): bool =
|
proc isKeyword(w: PIdent): bool =
|
||||||
# Nim and C++ share some keywords
|
# Nim and C++ share some keywords
|
||||||
|
|
@ -34,23 +36,27 @@ proc mangleField(m: BModule; name: PIdent): string =
|
||||||
if isKeyword(name):
|
if isKeyword(name):
|
||||||
result.add "_0"
|
result.add "_0"
|
||||||
|
|
||||||
proc fillBackendName(m: BModule; s: PSym) =
|
proc mangleName(m: BModule; s: PSym): Rope =
|
||||||
if s.loc.r == "":
|
result = s.loc.r
|
||||||
var result = s.name.s.mangle.rope
|
if result == nil:
|
||||||
result.add "__"
|
result = s.name.s.mangle.rope
|
||||||
|
result.add "_"
|
||||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||||
result.add "_"
|
result.add "_"
|
||||||
result.add rope s.itemId.item
|
result.add rope s.itemId.item
|
||||||
if m.hcrOn:
|
if m.hcrOn:
|
||||||
result.add "_"
|
result.add "_"
|
||||||
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
|
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts))
|
||||||
s.loc.r = result
|
s.loc.r = result
|
||||||
writeMangledName(m.ndi, s, m.config)
|
writeMangledName(m.ndi, s, m.config)
|
||||||
|
|
||||||
proc fillParamName(m: BModule; s: PSym) =
|
proc mangleParamName(m: BModule; s: PSym): Rope =
|
||||||
if s.loc.r == "":
|
## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
|
||||||
|
## Fortunately C's scoping rules are sane enough so that that doesn't
|
||||||
|
## cause any trouble.
|
||||||
|
result = s.loc.r
|
||||||
|
if result == nil:
|
||||||
var res = s.name.s.mangle
|
var res = s.name.s.mangle
|
||||||
res.add idOrSig(s, res, m.sigConflicts, m.config)
|
|
||||||
# Take into account if HCR is on because of the following scenario:
|
# Take into account if HCR is on because of the following scenario:
|
||||||
# if a module gets imported and it has some more importc symbols in it,
|
# if a module gets imported and it has some more importc symbols in it,
|
||||||
# some param names might receive the "_0" suffix to distinguish from what
|
# some param names might receive the "_0" suffix to distinguish from what
|
||||||
|
|
@ -67,16 +73,21 @@ proc fillParamName(m: BModule; s: PSym) =
|
||||||
# and a function called in main or proxy uses `socket` as a parameter name.
|
# and a function called in main or proxy uses `socket` as a parameter name.
|
||||||
# That would lead to either needing to reload `proxy` or to overwrite the
|
# That would lead to either needing to reload `proxy` or to overwrite the
|
||||||
# executable file for the main module, which is running (or both!) -> error.
|
# executable file for the main module, which is running (or both!) -> error.
|
||||||
s.loc.r = res.rope
|
if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res):
|
||||||
|
res.add "_0"
|
||||||
|
result = res.rope
|
||||||
|
s.loc.r = result
|
||||||
writeMangledName(m.ndi, s, m.config)
|
writeMangledName(m.ndi, s, m.config)
|
||||||
|
|
||||||
proc fillLocalName(p: BProc; s: PSym) =
|
proc mangleLocalName(p: BProc; s: PSym): Rope =
|
||||||
assert s.kind in skLocalVars+{skTemp}
|
assert s.kind in skLocalVars+{skTemp}
|
||||||
#assert sfGlobal notin s.flags
|
#assert sfGlobal notin s.flags
|
||||||
if s.loc.r == "":
|
result = s.loc.r
|
||||||
|
if result == nil:
|
||||||
var key = s.name.s.mangle
|
var key = s.name.s.mangle
|
||||||
|
shallow(key)
|
||||||
let counter = p.sigConflicts.getOrDefault(key)
|
let counter = p.sigConflicts.getOrDefault(key)
|
||||||
var result = key.rope
|
result = key.rope
|
||||||
if s.kind == skTemp:
|
if s.kind == skTemp:
|
||||||
# speed up conflict search for temps (these are quite common):
|
# speed up conflict search for temps (these are quite common):
|
||||||
if counter != 0: result.add "_" & rope(counter+1)
|
if counter != 0: result.add "_" & rope(counter+1)
|
||||||
|
|
@ -92,6 +103,7 @@ proc scopeMangledParam(p: BProc; param: PSym) =
|
||||||
## generate unique identifiers reliably (consider that ``var a = a`` is
|
## generate unique identifiers reliably (consider that ``var a = a`` is
|
||||||
## even an idiom in Nim).
|
## even an idiom in Nim).
|
||||||
var key = param.name.s.mangle
|
var key = param.name.s.mangle
|
||||||
|
shallow(key)
|
||||||
p.sigConflicts.inc(key)
|
p.sigConflicts.inc(key)
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -99,12 +111,13 @@ const
|
||||||
tyDistinct, tyRange, tyStatic, tyAlias, tySink,
|
tyDistinct, tyRange, tyStatic, tyAlias, tySink,
|
||||||
tyInferred, tyOwned}
|
tyInferred, tyOwned}
|
||||||
|
|
||||||
proc typeName(typ: PType; result: var Rope) =
|
proc typeName(typ: PType): Rope =
|
||||||
let typ = typ.skipTypes(irrelevantForBackend)
|
let typ = typ.skipTypes(irrelevantForBackend)
|
||||||
result.add $typ.kind
|
result =
|
||||||
if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
|
if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
|
||||||
result.add "_"
|
rope($typ.kind & '_' & typ.sym.name.s.mangle)
|
||||||
result.add typ.sym.name.s.mangle
|
else:
|
||||||
|
rope($typ.kind)
|
||||||
|
|
||||||
proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
|
proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
|
||||||
var t = typ
|
var t = typ
|
||||||
|
|
@ -117,17 +130,14 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ
|
let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ
|
||||||
if typ.loc.r == "":
|
if typ.loc.r == nil:
|
||||||
typ.typeName(typ.loc.r)
|
typ.loc.r = typ.typeName & $sig
|
||||||
typ.loc.r.add $sig
|
|
||||||
else:
|
else:
|
||||||
when defined(debugSigHashes):
|
when defined(debugSigHashes):
|
||||||
# check consistency:
|
# check consistency:
|
||||||
var tn = newRopeAppender()
|
assert($typ.loc.r == $(typ.typeName & $sig))
|
||||||
typ.typeName(tn)
|
|
||||||
assert($typ.loc.r == $(tn & $sig))
|
|
||||||
result = typ.loc.r
|
result = typ.loc.r
|
||||||
if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
|
if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
|
||||||
|
|
||||||
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
|
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
|
||||||
case int(getSize(conf, typ))
|
case int(getSize(conf, typ))
|
||||||
|
|
@ -206,19 +216,12 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} =
|
||||||
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
|
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
|
||||||
(typ[0] == nil) or isPureObject(typ))
|
(typ[0] == nil) or isPureObject(typ))
|
||||||
|
|
||||||
proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
|
proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool =
|
||||||
# Arrays and sets cannot be returned by a C procedure, because C is
|
# Arrays and sets cannot be returned by a C procedure, because C is
|
||||||
# such a poor programming language.
|
# such a poor programming language.
|
||||||
# We exclude records with refs too. This enhances efficiency and
|
# We exclude records with refs too. This enhances efficiency and
|
||||||
# is necessary for proper code generation of assignments.
|
# is necessary for proper code generation of assignments.
|
||||||
var rettype = typ
|
if rettype == nil: result = true
|
||||||
var isAllowedCall = true
|
|
||||||
if isProc:
|
|
||||||
rettype = rettype[0]
|
|
||||||
isAllowedCall = typ.callConv in {ccClosure, ccInline, ccNimCall}
|
|
||||||
if rettype == nil or (isAllowedCall and
|
|
||||||
getSize(conf, rettype) > conf.target.floatSize*3):
|
|
||||||
result = true
|
|
||||||
else:
|
else:
|
||||||
case mapType(conf, rettype, skResult)
|
case mapType(conf, rettype, skResult)
|
||||||
of ctArray:
|
of ctArray:
|
||||||
|
|
@ -253,12 +256,14 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) =
|
||||||
m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope])
|
m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope])
|
||||||
# see `testCodegenABICheck` for example error message it generates
|
# see `testCodegenABICheck` for example error message it generates
|
||||||
|
|
||||||
|
from ccgutils import ccgIntroducedPtr
|
||||||
|
export ccgIntroducedPtr
|
||||||
|
|
||||||
proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
|
proc fillResult(conf: ConfigRef; param: PNode) =
|
||||||
fillLoc(param.sym.loc, locParam, param, "Result",
|
fillLoc(param.sym.loc, locParam, param, ~"Result",
|
||||||
OnStack)
|
OnStack)
|
||||||
let t = param.sym.typ
|
let t = param.sym.typ
|
||||||
if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype):
|
if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t):
|
||||||
incl(param.sym.loc.flags, lfIndirect)
|
incl(param.sym.loc.flags, lfIndirect)
|
||||||
param.sym.loc.storage = OnUnknown
|
param.sym.loc.storage = OnUnknown
|
||||||
|
|
||||||
|
|
@ -281,11 +286,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
|
||||||
of tyString:
|
of tyString:
|
||||||
case detectStrVersion(m)
|
case detectStrVersion(m)
|
||||||
of 2:
|
of 2:
|
||||||
cgsym(m, "NimStrPayload")
|
discard cgsym(m, "NimStrPayload")
|
||||||
cgsym(m, "NimStringV2")
|
discard cgsym(m, "NimStringV2")
|
||||||
result = typeNameOrLiteral(m, typ, "NimStringV2")
|
result = typeNameOrLiteral(m, typ, "NimStringV2")
|
||||||
else:
|
else:
|
||||||
cgsym(m, "NimStringDesc")
|
discard cgsym(m, "NimStringDesc")
|
||||||
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
|
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
|
||||||
of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING")
|
of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING")
|
||||||
of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL")
|
of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL")
|
||||||
|
|
@ -299,11 +304,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
|
||||||
else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
|
else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
|
||||||
of tyGenericInst, tyAlias, tySink, tyOwned:
|
of tyGenericInst, tyAlias, tySink, tyOwned:
|
||||||
result = getSimpleTypeDesc(m, lastSon typ)
|
result = getSimpleTypeDesc(m, lastSon typ)
|
||||||
else: result = ""
|
else: result = nil
|
||||||
|
|
||||||
if result != "" and typ.isImportedType():
|
if result != nil and typ.isImportedType():
|
||||||
let sig = hashType(typ, m.config)
|
let sig = hashType typ
|
||||||
if cacheGetType(m.typeCache, sig) == "":
|
if cacheGetType(m.typeCache, sig) == nil:
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
|
|
||||||
proc pushType(m: BModule, typ: PType) =
|
proc pushType(m: BModule, typ: PType) =
|
||||||
|
|
@ -316,7 +321,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||||
if typ == nil: result = rope("void")
|
if typ == nil: result = rope("void")
|
||||||
else:
|
else:
|
||||||
result = getSimpleTypeDesc(m, typ)
|
result = getSimpleTypeDesc(m, typ)
|
||||||
if result == "": result = cacheGetType(m.typeCache, sig)
|
if result == nil: result = cacheGetType(m.typeCache, sig)
|
||||||
|
|
||||||
proc structOrUnion(t: PType): Rope =
|
proc structOrUnion(t: PType): Rope =
|
||||||
let cachedUnion = rope("union")
|
let cachedUnion = rope("union")
|
||||||
|
|
@ -337,9 +342,9 @@ proc seqStar(m: BModule): string =
|
||||||
|
|
||||||
proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
|
proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||||
result = cacheGetType(m.forwTypeCache, sig)
|
result = cacheGetType(m.forwTypeCache, sig)
|
||||||
if result != "": return
|
if result != nil: return
|
||||||
result = getTypePre(m, typ, sig)
|
result = getTypePre(m, typ, sig)
|
||||||
if result != "": return
|
if result != nil: return
|
||||||
let concrete = typ.skipTypes(abstractInst)
|
let concrete = typ.skipTypes(abstractInst)
|
||||||
case concrete.kind
|
case concrete.kind
|
||||||
of tySequence, tyTuple, tyObject:
|
of tySequence, tyTuple, tyObject:
|
||||||
|
|
@ -362,16 +367,16 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R
|
||||||
if isImportedCppType(etB) and t.kind == tyGenericInst:
|
if isImportedCppType(etB) and t.kind == tyGenericInst:
|
||||||
result = getTypeDescAux(m, t, check, kind)
|
result = getTypeDescAux(m, t, check, kind)
|
||||||
else:
|
else:
|
||||||
result = getTypeForward(m, t, hashType(t, m.config))
|
result = getTypeForward(m, t, hashType(t))
|
||||||
pushType(m, t)
|
pushType(m, t)
|
||||||
of tySequence:
|
of tySequence:
|
||||||
let sig = hashType(t, m.config)
|
let sig = hashType(t)
|
||||||
if optSeqDestructors in m.config.globalOptions:
|
if optSeqDestructors in m.config.globalOptions:
|
||||||
if skipTypes(etB[0], typedescInst).kind == tyEmpty:
|
if skipTypes(etB[0], typedescInst).kind == tyEmpty:
|
||||||
internalError(m.config, "cannot map the empty seq type to a C type")
|
internalError(m.config, "cannot map the empty seq type to a C type")
|
||||||
|
|
||||||
result = cacheGetType(m.forwTypeCache, sig)
|
result = cacheGetType(m.forwTypeCache, sig)
|
||||||
if result == "":
|
if result == nil:
|
||||||
result = getTypeName(m, t, sig)
|
result = getTypeName(m, t, sig)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
m.forwTypeCache[sig] = result
|
m.forwTypeCache[sig] = result
|
||||||
|
|
@ -379,7 +384,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R
|
||||||
let payload = result & "_Content"
|
let payload = result & "_Content"
|
||||||
addForwardStructFormat(m, rope"struct", payload)
|
addForwardStructFormat(m, rope"struct", payload)
|
||||||
|
|
||||||
if cacheGetType(m.typeCache, sig) == "":
|
if cacheGetType(m.typeCache, sig) == nil:
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
|
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
|
||||||
appcg(m, m.s[cfsTypes],
|
appcg(m, m.s[cfsTypes],
|
||||||
|
|
@ -398,9 +403,9 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope =
|
||||||
#result = getTypeForward(m, t, hashType(t)) & "_Content"
|
#result = getTypeForward(m, t, hashType(t)) & "_Content"
|
||||||
|
|
||||||
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
|
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
|
||||||
let sig = hashType(t, m.config)
|
let sig = hashType(t)
|
||||||
let result = cacheGetType(m.typeCache, sig)
|
let result = cacheGetType(m.typeCache, sig)
|
||||||
if result == "":
|
if result == nil:
|
||||||
discard getTypeDescAux(m, t, check, skVar)
|
discard getTypeDescAux(m, t, check, skVar)
|
||||||
else:
|
else:
|
||||||
# little hack for now to prevent multiple definitions of the same
|
# little hack for now to prevent multiple definitions of the same
|
||||||
|
|
@ -422,31 +427,30 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
|
||||||
proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
||||||
check: var IntSet, declareEnvironment=true;
|
check: var IntSet, declareEnvironment=true;
|
||||||
weakDep=false) =
|
weakDep=false) =
|
||||||
params = "("
|
params = nil
|
||||||
if t[0] == nil or isInvalidReturnType(m.config, t):
|
if t[0] == nil or isInvalidReturnType(m.config, t[0]):
|
||||||
rettype = "void"
|
rettype = ~"void"
|
||||||
else:
|
else:
|
||||||
rettype = getTypeDescAux(m, t[0], check, skResult)
|
rettype = getTypeDescAux(m, t[0], check, skResult)
|
||||||
for i in 1..<t.n.len:
|
for i in 1..<t.n.len:
|
||||||
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
|
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
|
||||||
var param = t.n[i].sym
|
var param = t.n[i].sym
|
||||||
if isCompileTimeOnly(param.typ): continue
|
if isCompileTimeOnly(param.typ): continue
|
||||||
if params != "(": params.add(", ")
|
if params != nil: params.add(~", ")
|
||||||
fillParamName(m, param)
|
fillLoc(param.loc, locParam, t.n[i], mangleParamName(m, param),
|
||||||
fillLoc(param.loc, locParam, t.n[i],
|
|
||||||
param.paramStorageLoc)
|
param.paramStorageLoc)
|
||||||
if ccgIntroducedPtr(m.config, param, t[0]):
|
if ccgIntroducedPtr(m.config, param, t[0]):
|
||||||
params.add(getTypeDescWeak(m, param.typ, check, skParam))
|
params.add(getTypeDescWeak(m, param.typ, check, skParam))
|
||||||
params.add("*")
|
params.add(~"*")
|
||||||
incl(param.loc.flags, lfIndirect)
|
incl(param.loc.flags, lfIndirect)
|
||||||
param.loc.storage = OnUnknown
|
param.loc.storage = OnUnknown
|
||||||
elif weakDep:
|
elif weakDep:
|
||||||
params.add(getTypeDescWeak(m, param.typ, check, skParam))
|
params.add(getTypeDescWeak(m, param.typ, check, skParam))
|
||||||
else:
|
else:
|
||||||
params.add(getTypeDescAux(m, param.typ, check, skParam))
|
params.add(getTypeDescAux(m, param.typ, check, skParam))
|
||||||
params.add(" ")
|
params.add(~" ")
|
||||||
if sfNoalias in param.flags:
|
if sfNoalias in param.flags:
|
||||||
params.add("NIM_NOALIAS ")
|
params.add(~"NIM_NOALIAS ")
|
||||||
params.add(param.loc.r)
|
params.add(param.loc.r)
|
||||||
# declare the len field for open arrays:
|
# declare the len field for open arrays:
|
||||||
var arr = param.typ.skipTypes({tyGenericInst})
|
var arr = param.typ.skipTypes({tyGenericInst})
|
||||||
|
|
@ -459,58 +463,54 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
|
||||||
params.addf(", NI $1Len_$2", [param.loc.r, j.rope])
|
params.addf(", NI $1Len_$2", [param.loc.r, j.rope])
|
||||||
inc(j)
|
inc(j)
|
||||||
arr = arr[0].skipTypes({tySink})
|
arr = arr[0].skipTypes({tySink})
|
||||||
if t[0] != nil and isInvalidReturnType(m.config, t):
|
if t[0] != nil and isInvalidReturnType(m.config, t[0]):
|
||||||
var arr = t[0]
|
var arr = t[0]
|
||||||
if params != "(": params.add(", ")
|
if params != nil: params.add(", ")
|
||||||
if mapReturnType(m.config, t[0]) != ctArray:
|
if mapReturnType(m.config, t[0]) != ctArray:
|
||||||
if isHeaderFile in m.flags:
|
params.add(getTypeDescWeak(m, arr, check, skResult))
|
||||||
# still generates types for `--header`
|
params.add("*")
|
||||||
params.add(getTypeDescAux(m, arr, check, skResult))
|
|
||||||
params.add("*")
|
|
||||||
else:
|
|
||||||
params.add(getTypeDescWeak(m, arr, check, skResult))
|
|
||||||
params.add("*")
|
|
||||||
else:
|
else:
|
||||||
params.add(getTypeDescAux(m, arr, check, skResult))
|
params.add(getTypeDescAux(m, arr, check, skResult))
|
||||||
params.addf(" Result", [])
|
params.addf(" Result", [])
|
||||||
if t.callConv == ccClosure and declareEnvironment:
|
if t.callConv == ccClosure and declareEnvironment:
|
||||||
if params != "(": params.add(", ")
|
if params != nil: params.add(", ")
|
||||||
params.add("void* ClE_0")
|
params.add("void* ClE_0")
|
||||||
if tfVarargs in t.flags:
|
if tfVarargs in t.flags:
|
||||||
if params != "(": params.add(", ")
|
if params != nil: params.add(", ")
|
||||||
params.add("...")
|
params.add("...")
|
||||||
if params == "(": params.add("void)")
|
if params == nil: params.add("void)")
|
||||||
else: params.add(")")
|
else: params.add(")")
|
||||||
|
params = "(" & params
|
||||||
|
|
||||||
proc mangleRecFieldName(m: BModule; field: PSym): Rope =
|
proc mangleRecFieldName(m: BModule; field: PSym): Rope =
|
||||||
if {sfImportc, sfExportc} * field.flags != {}:
|
if {sfImportc, sfExportc} * field.flags != {}:
|
||||||
result = field.loc.r
|
result = field.loc.r
|
||||||
else:
|
else:
|
||||||
result = rope(mangleField(m, field.name))
|
result = rope(mangleField(m, field.name))
|
||||||
if result == "": internalError(m.config, field.info, "mangleRecFieldName")
|
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
|
||||||
|
|
||||||
proc genRecordFieldsAux(m: BModule, n: PNode,
|
proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
rectype: PType,
|
rectype: PType,
|
||||||
check: var IntSet; result: var Rope; unionPrefix = "") =
|
check: var IntSet, unionPrefix = ""): Rope =
|
||||||
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
genRecordFieldsAux(m, n[i], rectype, check, result, unionPrefix)
|
result.add(genRecordFieldsAux(m, n[i], rectype, check, unionPrefix))
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
if n[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
|
if n[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
|
||||||
genRecordFieldsAux(m, n[0], rectype, check, result, unionPrefix)
|
result.add(genRecordFieldsAux(m, n[0], rectype, check, unionPrefix))
|
||||||
# prefix mangled name with "_U" to avoid clashes with other field names,
|
# prefix mangled name with "_U" to avoid clashes with other field names,
|
||||||
# since identifiers are not allowed to start with '_'
|
# since identifiers are not allowed to start with '_'
|
||||||
var unionBody: Rope = ""
|
var unionBody: Rope = nil
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
case n[i].kind
|
case n[i].kind
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
let k = lastSon(n[i])
|
let k = lastSon(n[i])
|
||||||
if k.kind != nkSym:
|
if k.kind != nkSym:
|
||||||
let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
|
let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
|
||||||
var a = newRopeAppender()
|
let a = genRecordFieldsAux(m, k, rectype, check, unionPrefix & $structName & ".")
|
||||||
genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
|
if a != nil:
|
||||||
if a != "":
|
|
||||||
if tfPacked notin rectype.flags:
|
if tfPacked notin rectype.flags:
|
||||||
unionBody.add("struct {")
|
unionBody.add("struct {")
|
||||||
else:
|
else:
|
||||||
|
|
@ -523,9 +523,9 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||||
unionBody.addf("#pragma pack(pop)$n", [])
|
unionBody.addf("#pragma pack(pop)$n", [])
|
||||||
else:
|
else:
|
||||||
genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
|
unionBody.add(genRecordFieldsAux(m, k, rectype, check, unionPrefix))
|
||||||
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
||||||
if unionBody != "":
|
if unionBody != nil:
|
||||||
result.addf("union{$n$1};$n", [unionBody])
|
result.addf("union{$n$1};$n", [unionBody])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = n.sym
|
let field = n.sym
|
||||||
|
|
@ -539,7 +539,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||||
# with heavily templatized C++ code:
|
# with heavily templatized C++ code:
|
||||||
if not isImportedCppType(rectype):
|
if not isImportedCppType(rectype):
|
||||||
let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
|
let noAlias = if sfNoalias in field.flags: ~" NIM_NOALIAS" else: nil
|
||||||
|
|
||||||
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
|
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
|
||||||
if fieldType.kind == tyUncheckedArray:
|
if fieldType.kind == tyUncheckedArray:
|
||||||
|
|
@ -557,8 +557,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
else: internalError(m.config, n.info, "genRecordFieldsAux()")
|
||||||
|
|
||||||
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
|
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
|
||||||
result = newRopeAppender()
|
result = genRecordFieldsAux(m, typ.n, typ, check)
|
||||||
genRecordFieldsAux(m, typ.n, typ, check, result)
|
|
||||||
|
|
||||||
proc fillObjectFields*(m: BModule; typ: PType) =
|
proc fillObjectFields*(m: BModule; typ: PType) =
|
||||||
# sometimes generic objects are not consistently merged. We patch over
|
# sometimes generic objects are not consistently merged. We patch over
|
||||||
|
|
@ -586,7 +585,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
|
||||||
|
|
||||||
if typ.kind == tyObject:
|
if typ.kind == tyObject:
|
||||||
if typ[0] == nil:
|
if typ[0] == nil:
|
||||||
if lacksMTypeField(typ):
|
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
||||||
appcg(m, result, " {$n", [])
|
appcg(m, result, " {$n", [])
|
||||||
else:
|
else:
|
||||||
if optTinyRtti in m.config.globalOptions:
|
if optTinyRtti in m.config.globalOptions:
|
||||||
|
|
@ -615,7 +614,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
|
||||||
result.addf(" {$n", [name])
|
result.addf(" {$n", [name])
|
||||||
|
|
||||||
let desc = getRecordFields(m, typ, check)
|
let desc = getRecordFields(m, typ, check)
|
||||||
if desc == "" and not hasField:
|
if desc == nil and not hasField:
|
||||||
result.addf("char dummy;$n", [])
|
result.addf("char dummy;$n", [])
|
||||||
else:
|
else:
|
||||||
result.add(desc)
|
result.add(desc)
|
||||||
|
|
@ -626,11 +625,11 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
|
||||||
proc getTupleDesc(m: BModule, typ: PType, name: Rope,
|
proc getTupleDesc(m: BModule, typ: PType, name: Rope,
|
||||||
check: var IntSet): Rope =
|
check: var IntSet): Rope =
|
||||||
result = "$1 $2 {$n" % [structOrUnion(typ), name]
|
result = "$1 $2 {$n" % [structOrUnion(typ), name]
|
||||||
var desc: Rope = ""
|
var desc: Rope = nil
|
||||||
for i in 0..<typ.len:
|
for i in 0..<typ.len:
|
||||||
desc.addf("$1 Field$2;$n",
|
desc.addf("$1 Field$2;$n",
|
||||||
[getTypeDescAux(m, typ[i], check, skField), rope(i)])
|
[getTypeDescAux(m, typ[i], check, skField), rope(i)])
|
||||||
if desc == "": result.add("char dummy;\L")
|
if desc == nil: result.add("char dummy;\L")
|
||||||
else: result.add(desc)
|
else: result.add(desc)
|
||||||
result.add("};\L")
|
result.add("};\L")
|
||||||
|
|
||||||
|
|
@ -664,12 +663,12 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
|
||||||
else: result.elemType
|
else: result.elemType
|
||||||
|
|
||||||
proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope =
|
proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope =
|
||||||
let sig = hashType(t, m.config)
|
let sig = hashType(t)
|
||||||
if kind == skParam:
|
if kind == skParam:
|
||||||
result = getTypeDescWeak(m, t[0], check, kind) & "*"
|
result = getTypeDescWeak(m, t[0], check, kind) & "*"
|
||||||
else:
|
else:
|
||||||
result = cacheGetType(m.typeCache, sig)
|
result = cacheGetType(m.typeCache, sig)
|
||||||
if result == "":
|
if result == nil:
|
||||||
result = getTypeName(m, t, sig)
|
result = getTypeName(m, t, sig)
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
let elemType = getTypeDescWeak(m, t[0], check, kind)
|
let elemType = getTypeDescWeak(m, t[0], check, kind)
|
||||||
|
|
@ -688,14 +687,14 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
# C type generation into an analysis and a code generation phase somehow.
|
# C type generation into an analysis and a code generation phase somehow.
|
||||||
if t.sym != nil: useHeader(m, t.sym)
|
if t.sym != nil: useHeader(m, t.sym)
|
||||||
if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
|
if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
|
||||||
let sig = hashType(origTyp, m.config)
|
let sig = hashType(origTyp)
|
||||||
|
|
||||||
defer: # defer is the simplest in this case
|
defer: # defer is the simplest in this case
|
||||||
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
|
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
|
||||||
addAbiCheck(m, t, result)
|
addAbiCheck(m, t, result)
|
||||||
|
|
||||||
result = getTypePre(m, t, sig)
|
result = getTypePre(m, t, sig)
|
||||||
if result != "" and t.kind != tyOpenArray:
|
if result != nil and t.kind != tyOpenArray:
|
||||||
excl(check, t.id)
|
excl(check, t.id)
|
||||||
return
|
return
|
||||||
case t.kind
|
case t.kind
|
||||||
|
|
@ -717,7 +716,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
result = getTypeDescAux(m, et, check, kind) & star
|
result = getTypeDescAux(m, et, check, kind) & star
|
||||||
else:
|
else:
|
||||||
# no restriction! We have a forward declaration for structs
|
# no restriction! We have a forward declaration for structs
|
||||||
let name = getTypeForward(m, et, hashType(et, m.config))
|
let name = getTypeForward(m, et, hashType et)
|
||||||
result = name & star
|
result = name & star
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
of tySequence:
|
of tySequence:
|
||||||
|
|
@ -726,7 +725,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
else:
|
else:
|
||||||
# no restriction! We have a forward declaration for structs
|
# no restriction! We have a forward declaration for structs
|
||||||
let name = getTypeForward(m, et, hashType(et, m.config))
|
let name = getTypeForward(m, et, hashType et)
|
||||||
result = name & seqStar(m) & star
|
result = name & seqStar(m) & star
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
pushType(m, et)
|
pushType(m, et)
|
||||||
|
|
@ -738,7 +737,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
result = getOpenArrayDesc(m, t, check, kind)
|
result = getOpenArrayDesc(m, t, check, kind)
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
result = cacheGetType(m.typeCache, sig)
|
result = cacheGetType(m.typeCache, sig)
|
||||||
if result == "":
|
if result == nil:
|
||||||
result = getTypeName(m, origTyp, sig)
|
result = getTypeName(m, origTyp, sig)
|
||||||
if not (isImportedCppType(t) or
|
if not (isImportedCppType(t) or
|
||||||
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
|
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
|
||||||
|
|
@ -786,12 +785,12 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
# we cannot use getTypeForward here because then t would be associated
|
# we cannot use getTypeForward here because then t would be associated
|
||||||
# with the name of the struct, not with the pointer to the struct:
|
# with the name of the struct, not with the pointer to the struct:
|
||||||
result = cacheGetType(m.forwTypeCache, sig)
|
result = cacheGetType(m.forwTypeCache, sig)
|
||||||
if result == "":
|
if result == nil:
|
||||||
result = getTypeName(m, origTyp, sig)
|
result = getTypeName(m, origTyp, sig)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
addForwardStructFormat(m, structOrUnion(t), result)
|
addForwardStructFormat(m, structOrUnion(t), result)
|
||||||
m.forwTypeCache[sig] = result
|
m.forwTypeCache[sig] = result
|
||||||
assert(cacheGetType(m.typeCache, sig) == "")
|
assert(cacheGetType(m.typeCache, sig) == nil)
|
||||||
m.typeCache[sig] = result & seqStar(m)
|
m.typeCache[sig] = result & seqStar(m)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
if skipTypes(t[0], typedescInst).kind != tyEmpty:
|
if skipTypes(t[0], typedescInst).kind != tyEmpty:
|
||||||
|
|
@ -826,42 +825,40 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
|
m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
|
||||||
[foo, result, rope(n)])
|
[foo, result, rope(n)])
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
let tt = origTyp.skipTypes({tyDistinct})
|
if isImportedCppType(t) and origTyp.kind == tyGenericInst:
|
||||||
if isImportedCppType(t) and tt.kind == tyGenericInst:
|
let cppName = getTypeName(m, t, sig)
|
||||||
let cppNameAsRope = getTypeName(m, t, sig)
|
|
||||||
let cppName = $cppNameAsRope
|
|
||||||
var i = 0
|
var i = 0
|
||||||
var chunkStart = 0
|
var chunkStart = 0
|
||||||
|
|
||||||
template addResultType(ty: untyped) =
|
template addResultType(ty: untyped) =
|
||||||
if ty == nil or ty.kind == tyVoid:
|
if ty == nil or ty.kind == tyVoid:
|
||||||
result.add("void")
|
result.add(~"void")
|
||||||
elif ty.kind == tyStatic:
|
elif ty.kind == tyStatic:
|
||||||
internalAssert m.config, ty.n != nil
|
internalAssert m.config, ty.n != nil
|
||||||
result.add ty.n.renderTree
|
result.add ty.n.renderTree
|
||||||
else:
|
else:
|
||||||
result.add getTypeDescAux(m, ty, check, kind)
|
result.add getTypeDescAux(m, ty, check, kind)
|
||||||
|
|
||||||
while i < cppName.len:
|
while i < cppName.data.len:
|
||||||
if cppName[i] == '\'':
|
if cppName.data[i] == '\'':
|
||||||
var chunkEnd = i-1
|
var chunkEnd = i-1
|
||||||
var idx, stars: int
|
var idx, stars: int
|
||||||
if scanCppGenericSlot(cppName, i, idx, stars):
|
if scanCppGenericSlot(cppName.data, i, idx, stars):
|
||||||
result.add cppName.substr(chunkStart, chunkEnd)
|
result.add cppName.data.substr(chunkStart, chunkEnd)
|
||||||
chunkStart = i
|
chunkStart = i
|
||||||
|
|
||||||
let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars)
|
let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars)
|
||||||
addResultType(typeInSlot)
|
addResultType(typeInSlot)
|
||||||
else:
|
else:
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
if chunkStart != 0:
|
if chunkStart != 0:
|
||||||
result.add cppName.substr(chunkStart)
|
result.add cppName.data.substr(chunkStart)
|
||||||
else:
|
else:
|
||||||
result = cppNameAsRope & "<"
|
result = cppName & "<"
|
||||||
for i in 1..<tt.len-1:
|
for i in 1..<origTyp.len-1:
|
||||||
if i > 1: result.add(" COMMA ")
|
if i > 1: result.add(" COMMA ")
|
||||||
addResultType(tt[i])
|
addResultType(origTyp[i])
|
||||||
result.add("> ")
|
result.add("> ")
|
||||||
# always call for sideeffects:
|
# always call for sideeffects:
|
||||||
assert t.kind != tyTuple
|
assert t.kind != tyTuple
|
||||||
|
|
@ -869,13 +866,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
# The resulting type will include commas and these won't play well
|
# The resulting type will include commas and these won't play well
|
||||||
# with the C macros for defining procs such as N_NIMCALL. We must
|
# with the C macros for defining procs such as N_NIMCALL. We must
|
||||||
# create a typedef for the type and use it in the proc signature:
|
# create a typedef for the type and use it in the proc signature:
|
||||||
let typedefName = "TY" & $sig
|
let typedefName = ~"TY" & $sig
|
||||||
m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
|
m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
|
||||||
m.typeCache[sig] = typedefName
|
m.typeCache[sig] = typedefName
|
||||||
result = typedefName
|
result = typedefName
|
||||||
else:
|
else:
|
||||||
result = cacheGetType(m.forwTypeCache, sig)
|
result = cacheGetType(m.forwTypeCache, sig)
|
||||||
if result == "":
|
if result == nil:
|
||||||
result = getTypeName(m, origTyp, sig)
|
result = getTypeName(m, origTyp, sig)
|
||||||
m.forwTypeCache[sig] = result
|
m.forwTypeCache[sig] = result
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
|
|
@ -891,9 +888,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
discard # addAbiCheck(m, t, result) # already handled elsewhere
|
discard # addAbiCheck(m, t, result) # already handled elsewhere
|
||||||
of tySet:
|
of tySet:
|
||||||
# Don't use the imported name as it may be scoped: 'Foo::SomeKind'
|
# Don't use the imported name as it may be scoped: 'Foo::SomeKind'
|
||||||
result = rope("tySet_")
|
result = $t.kind & '_' & t.lastSon.typeName & $t.lastSon.hashType
|
||||||
t.lastSon.typeName(result)
|
|
||||||
result.add $t.lastSon.hashType(m.config)
|
|
||||||
m.typeCache[sig] = result
|
m.typeCache[sig] = result
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
let s = int(getSize(m.config, t))
|
let s = int(getSize(m.config, t))
|
||||||
|
|
@ -906,7 +901,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
|
||||||
result = getTypeDescAux(m, lastSon(t), check, kind)
|
result = getTypeDescAux(m, lastSon(t), check, kind)
|
||||||
else:
|
else:
|
||||||
internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
|
internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
|
||||||
result = ""
|
result = nil
|
||||||
# fixes bug #145:
|
# fixes bug #145:
|
||||||
excl(check, t.id)
|
excl(check, t.id)
|
||||||
|
|
||||||
|
|
@ -957,12 +952,21 @@ proc isReloadable(m: BModule, prc: PSym): bool =
|
||||||
proc isNonReloadable(m: BModule, prc: PSym): bool =
|
proc isNonReloadable(m: BModule, prc: PSym): bool =
|
||||||
return m.hcrOn and sfNonReloadable in prc.flags
|
return m.hcrOn and sfNonReloadable in prc.flags
|
||||||
|
|
||||||
proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) =
|
proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope =
|
||||||
|
var
|
||||||
|
rettype, params: Rope
|
||||||
# using static is needed for inline procs
|
# using static is needed for inline procs
|
||||||
|
if lfExportLib in prc.loc.flags:
|
||||||
|
if isHeaderFile in m.flags:
|
||||||
|
result.add "N_LIB_IMPORT "
|
||||||
|
else:
|
||||||
|
result.add "N_LIB_EXPORT "
|
||||||
|
elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc):
|
||||||
|
result.add "static "
|
||||||
|
elif sfImportc notin prc.flags:
|
||||||
|
result.add "N_LIB_PRIVATE "
|
||||||
var check = initIntSet()
|
var check = initIntSet()
|
||||||
fillBackendName(m, prc)
|
fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown)
|
||||||
fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
|
|
||||||
var rettype, params: Rope
|
|
||||||
genProcParams(m, prc.typ, rettype, params, check)
|
genProcParams(m, prc.typ, rettype, params, check)
|
||||||
# handle the 2 options for hotcodereloading codegen - function pointer
|
# handle the 2 options for hotcodereloading codegen - function pointer
|
||||||
# (instead of forward declaration) or header for function body with "_actual" postfix
|
# (instead of forward declaration) or header for function body with "_actual" postfix
|
||||||
|
|
@ -973,21 +977,12 @@ proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false)
|
||||||
# careful here! don't access ``prc.ast`` as that could reload large parts of
|
# careful here! don't access ``prc.ast`` as that could reload large parts of
|
||||||
# the object graph!
|
# the object graph!
|
||||||
if prc.constraint.isNil:
|
if prc.constraint.isNil:
|
||||||
if lfExportLib in prc.loc.flags:
|
|
||||||
if isHeaderFile in m.flags:
|
|
||||||
result.add "N_LIB_IMPORT "
|
|
||||||
else:
|
|
||||||
result.add "N_LIB_EXPORT "
|
|
||||||
elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc):
|
|
||||||
result.add "static "
|
|
||||||
elif sfImportc notin prc.flags:
|
|
||||||
result.add "N_LIB_PRIVATE "
|
|
||||||
result.addf("$1$2($3, $4)$5",
|
result.addf("$1$2($3, $4)$5",
|
||||||
[rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
|
[rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
|
||||||
params])
|
params])
|
||||||
else:
|
else:
|
||||||
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
|
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
|
||||||
result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
|
result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
|
||||||
|
|
||||||
# ------------------ type info generation -------------------------------------
|
# ------------------ type info generation -------------------------------------
|
||||||
|
|
||||||
|
|
@ -1026,7 +1021,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||||
#else echo("can contain a cycle: " & typeToString(typ))
|
#else echo("can contain a cycle: " & typeToString(typ))
|
||||||
if flags != 0:
|
if flags != 0:
|
||||||
m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)])
|
m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)])
|
||||||
cgsym(m, "TNimType")
|
discard cgsym(m, "TNimType")
|
||||||
if isDefined(m.config, "nimTypeNames"):
|
if isDefined(m.config, "nimTypeNames"):
|
||||||
var typename = typeToString(if origType.typeInst != nil: origType.typeInst
|
var typename = typeToString(if origType.typeInst != nil: origType.typeInst
|
||||||
else: origType, preferName)
|
else: origType, preferName)
|
||||||
|
|
@ -1034,16 +1029,16 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||||
typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
|
typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
|
||||||
m.s[cfsTypeInit3].addf("$1.name = $2;$n",
|
m.s[cfsTypeInit3].addf("$1.name = $2;$n",
|
||||||
[nameHcr, makeCString typename])
|
[nameHcr, makeCString typename])
|
||||||
cgsym(m, "nimTypeRoot")
|
discard cgsym(m, "nimTypeRoot")
|
||||||
m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
|
m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
|
||||||
[nameHcr])
|
[nameHcr])
|
||||||
|
|
||||||
if m.hcrOn:
|
if m.hcrOn:
|
||||||
m.s[cfsStrData].addf("static TNimType* $1;$n", [name])
|
m.s[cfsData].addf("static TNimType* $1;$n", [name])
|
||||||
m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
|
m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($2, \"$1\", sizeof(TNimType), NULL, (void**)&$1);$n",
|
||||||
[name, getModuleDllPath(m, m.module)])
|
[name, getModuleDllPath(m, m.module)])
|
||||||
else:
|
else:
|
||||||
m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
|
m.s[cfsData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
|
||||||
|
|
||||||
proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
|
proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
|
||||||
info: TLineInfo) =
|
info: TLineInfo) =
|
||||||
|
|
@ -1066,12 +1061,12 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
|
||||||
objtype = objtype[0].skipTypes(abstractPtrs)
|
objtype = objtype[0].skipTypes(abstractPtrs)
|
||||||
if objtype.sym == nil:
|
if objtype.sym == nil:
|
||||||
internalError(m.config, d.info, "anonymous obj with discriminator")
|
internalError(m.config, d.info, "anonymous obj with discriminator")
|
||||||
result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)]
|
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
|
||||||
|
|
||||||
proc rope(arg: Int128): Rope = rope($arg)
|
proc rope(arg: Int128): Rope = rope($arg)
|
||||||
|
|
||||||
proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
|
proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
|
||||||
cgsym(m, "TNimNode")
|
discard cgsym(m, "TNimNode")
|
||||||
var tmp = discriminatorTableName(m, objtype, d)
|
var tmp = discriminatorTableName(m, objtype, d)
|
||||||
result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
|
result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
|
||||||
|
|
||||||
|
|
@ -1106,7 +1101,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
|
||||||
var tmp = discriminatorTableName(m, typ, field)
|
var tmp = discriminatorTableName(m, typ, field)
|
||||||
var L = lengthOrd(m.config, field.typ)
|
var L = lengthOrd(m.config, field.typ)
|
||||||
assert L > 0
|
assert L > 0
|
||||||
if field.loc.r == "": fillObjectFields(m, typ)
|
if field.loc.r == nil: fillObjectFields(m, typ)
|
||||||
if field.loc.t == nil:
|
if field.loc.t == nil:
|
||||||
internalError(m.config, n.info, "genObjectFields")
|
internalError(m.config, n.info, "genObjectFields")
|
||||||
m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
|
m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
|
||||||
|
|
@ -1144,7 +1139,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
|
||||||
# Do not produce code for void types
|
# Do not produce code for void types
|
||||||
if isEmptyType(field.typ): return
|
if isEmptyType(field.typ): return
|
||||||
if field.bitsize == 0:
|
if field.bitsize == 0:
|
||||||
if field.loc.r == "": fillObjectFields(m, typ)
|
if field.loc.r == nil: fillObjectFields(m, typ)
|
||||||
if field.loc.t == nil:
|
if field.loc.t == nil:
|
||||||
internalError(m.config, n.info, "genObjectFields")
|
internalError(m.config, n.info, "genObjectFields")
|
||||||
m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
|
m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
|
||||||
|
|
@ -1238,7 +1233,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||||
assert(typ[0] != nil)
|
assert(typ[0] != nil)
|
||||||
genTypeInfoAux(m, typ, typ, name, info)
|
genTypeInfoAux(m, typ, typ, name, info)
|
||||||
var tmp = getNimNode(m)
|
var tmp = getNimNode(m)
|
||||||
m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n$3.node = &$1;$n",
|
m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
|
||||||
[tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)])
|
[tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)])
|
||||||
|
|
||||||
proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||||
|
|
@ -1263,34 +1258,38 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
|
||||||
proc declareNimType(m: BModule, name: string; str: Rope, module: int) =
|
proc declareNimType(m: BModule, name: string; str: Rope, module: int) =
|
||||||
let nr = rope(name)
|
let nr = rope(name)
|
||||||
if m.hcrOn:
|
if m.hcrOn:
|
||||||
m.s[cfsStrData].addf("static $2* $1;$n", [str, nr])
|
m.s[cfsData].addf("static $2* $1;$n", [str, nr])
|
||||||
m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n",
|
m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n",
|
||||||
[str, getModuleDllPath(m, module), nr])
|
[str, getModuleDllPath(m, module), nr])
|
||||||
else:
|
else:
|
||||||
m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr])
|
m.s[cfsData].addf("extern $2 $1;$n", [str, nr])
|
||||||
|
|
||||||
proc genTypeInfo2Name(m: BModule; t: PType): Rope =
|
proc genTypeInfo2Name(m: BModule; t: PType): Rope =
|
||||||
|
var res = "|"
|
||||||
var it = t
|
var it = t
|
||||||
it = it.skipTypes(skipPtrs)
|
while it != nil:
|
||||||
if it.sym != nil and tfFromGeneric notin it.flags:
|
it = it.skipTypes(skipPtrs)
|
||||||
var m = it.sym.owner
|
if it.sym != nil:
|
||||||
while m != nil and m.kind != skModule: m = m.owner
|
var m = it.sym.owner
|
||||||
if m == nil or sfSystemModule in m.flags:
|
while m != nil and m.kind != skModule: m = m.owner
|
||||||
# produce short names for system types:
|
if m == nil or sfSystemModule in m.flags:
|
||||||
result = it.sym.name.s
|
# produce short names for system types:
|
||||||
|
res.add it.sym.name.s
|
||||||
|
else:
|
||||||
|
var p = m.owner
|
||||||
|
if p != nil and p.kind == skPackage:
|
||||||
|
res.add p.name.s & "."
|
||||||
|
res.add m.name.s & "."
|
||||||
|
res.add it.sym.name.s
|
||||||
else:
|
else:
|
||||||
var p = m.owner
|
res.add $hashType(it)
|
||||||
if p != nil and p.kind == skPackage:
|
res.add "|"
|
||||||
result.add p.name.s & "."
|
it = it[0]
|
||||||
result.add m.name.s & "."
|
result = makeCString(res)
|
||||||
result.add it.sym.name.s
|
|
||||||
else:
|
|
||||||
result = $hashType(it, m.config)
|
|
||||||
result = makeCString(result)
|
|
||||||
|
|
||||||
proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
|
proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
|
||||||
|
|
||||||
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: var Rope) =
|
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
|
||||||
let theProc = getAttachedOp(m.g.graph, t, op)
|
let theProc = getAttachedOp(m.g.graph, t, op)
|
||||||
if theProc != nil and not isTrivialProc(m.g.graph, theProc):
|
if theProc != nil and not isTrivialProc(m.g.graph, theProc):
|
||||||
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
||||||
|
|
@ -1301,7 +1300,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
|
||||||
theProc.name.s & " needs to have the 'nimcall' calling convention")
|
theProc.name.s & " needs to have the 'nimcall' calling convention")
|
||||||
|
|
||||||
genProc(m, theProc)
|
genProc(m, theProc)
|
||||||
result.add theProc.loc.r
|
result = theProc.loc.r
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
if not canFormAcycle(t) and op == attachedTrace:
|
if not canFormAcycle(t) and op == attachedTrace:
|
||||||
|
|
@ -1313,117 +1312,30 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
|
||||||
# unfortunately this check is wrong for an object type that only contains
|
# unfortunately this check is wrong for an object type that only contains
|
||||||
# .cursor fields like 'Node' inside 'cycleleak'.
|
# .cursor fields like 'Node' inside 'cycleleak'.
|
||||||
internalError(m.config, info, "no attached trace proc found")
|
internalError(m.config, info, "no attached trace proc found")
|
||||||
result.add rope("NIM_NIL")
|
result = rope("NIM_NIL")
|
||||||
|
|
||||||
proc getObjDepth(t: PType): int16 =
|
proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
|
||||||
var x = t
|
var typeName: Rope
|
||||||
result = -1
|
if t.kind in {tyObject, tyDistinct}:
|
||||||
while x != nil:
|
if incompleteType(t):
|
||||||
x = skipTypes(x, skipPtrs)
|
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
||||||
x = x[0]
|
typeToString(t))
|
||||||
inc(result)
|
typeName = genTypeInfo2Name(m, t)
|
||||||
|
else:
|
||||||
|
typeName = rope("NIM_NIL")
|
||||||
|
|
||||||
proc genDisplayElem(d: MD5Digest): uint32 =
|
discard cgsym(m, "TNimTypeV2")
|
||||||
result = 0
|
m.s[cfsData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
|
||||||
for i in 0..3:
|
let destroyImpl = genHook(m, t, info, attachedDestructor)
|
||||||
result += uint32(d[i])
|
let traceImpl = genHook(m, t, info, attachedTrace)
|
||||||
result = result shl 8
|
let disposeImpl = genHook(m, t, info, attachedDispose)
|
||||||
|
|
||||||
proc genDisplay(m: BModule, t: PType, depth: int): Rope =
|
|
||||||
result = Rope"{"
|
|
||||||
var x = t
|
|
||||||
var seqs = newSeq[string](depth+1)
|
|
||||||
var i = 0
|
|
||||||
while x != nil:
|
|
||||||
x = skipTypes(x, skipPtrs)
|
|
||||||
seqs[i] = $genDisplayElem(MD5Digest(hashType(x, m.config)))
|
|
||||||
x = x[0]
|
|
||||||
inc i
|
|
||||||
|
|
||||||
for i in countdown(depth, 1):
|
|
||||||
result.add seqs[i] & ", "
|
|
||||||
result.add seqs[0]
|
|
||||||
result.add "}"
|
|
||||||
|
|
||||||
proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
|
|
||||||
cgsym(m, "TNimTypeV2")
|
|
||||||
m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
|
|
||||||
|
|
||||||
var flags = 0
|
var flags = 0
|
||||||
if not canFormAcycle(t): flags = flags or 1
|
if not canFormAcycle(t): flags = flags or 1
|
||||||
|
|
||||||
var typeEntry = newRopeAppender()
|
addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.disposeImpl = (void*)$6; $1.flags = $7;", [
|
||||||
addf(typeEntry, "$1.destructor = (void*)", [name])
|
name, destroyImpl, getTypeDesc(m, t), typeName,
|
||||||
genHook(m, t, info, attachedDestructor, typeEntry)
|
traceImpl, disposeImpl, rope(flags)])
|
||||||
|
|
||||||
addf(typeEntry, "; $1.traceImpl = (void*)", [name])
|
|
||||||
genHook(m, t, info, attachedTrace, typeEntry)
|
|
||||||
|
|
||||||
let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
|
|
||||||
|
|
||||||
if t.kind in {tyObject, tyDistinct} and incompleteType(t):
|
|
||||||
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
|
||||||
typeToString(t))
|
|
||||||
|
|
||||||
if isDefined(m.config, "nimTypeNames"):
|
|
||||||
var typeName: Rope
|
|
||||||
if t.kind in {tyObject, tyDistinct}:
|
|
||||||
typeName = genTypeInfo2Name(m, t)
|
|
||||||
else:
|
|
||||||
typeName = rope("NIM_NIL")
|
|
||||||
addf(typeEntry, "; $1.name = $2", [name, typeName])
|
|
||||||
addf(typeEntry, "; $1.size = sizeof($2); $1.align = (NI16) NIM_ALIGNOF($2); $1.depth = $3; $1.flags = $4;",
|
|
||||||
[name, getTypeDesc(m, t), rope(objDepth), rope(flags)])
|
|
||||||
|
|
||||||
if objDepth >= 0:
|
|
||||||
let objDisplay = genDisplay(m, t, objDepth)
|
|
||||||
let objDisplayStore = getTempName(m)
|
|
||||||
m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay])
|
|
||||||
addf(typeEntry, "$1.display = $2;$n", [name, rope(objDisplayStore)])
|
|
||||||
|
|
||||||
m.s[cfsTypeInit3].add typeEntry
|
|
||||||
|
|
||||||
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
|
|
||||||
discard genTypeInfoV1(m, t, info)
|
|
||||||
|
|
||||||
proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
|
|
||||||
cgsym(m, "TNimTypeV2")
|
|
||||||
m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
|
|
||||||
|
|
||||||
var flags = 0
|
|
||||||
if not canFormAcycle(t): flags = flags or 1
|
|
||||||
|
|
||||||
var typeEntry = newRopeAppender()
|
|
||||||
addf(typeEntry, "N_LIB_PRIVATE TNimTypeV2 $1 = {", [name])
|
|
||||||
add(typeEntry, ".destructor = (void*)")
|
|
||||||
genHook(m, t, info, attachedDestructor, typeEntry)
|
|
||||||
|
|
||||||
let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
|
|
||||||
|
|
||||||
if t.kind in {tyObject, tyDistinct} and incompleteType(t):
|
|
||||||
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
|
||||||
typeToString(t))
|
|
||||||
|
|
||||||
addf(typeEntry, ", .size = sizeof($1), .align = (NI16) NIM_ALIGNOF($1), .depth = $2",
|
|
||||||
[getTypeDesc(m, t), rope(objDepth)])
|
|
||||||
|
|
||||||
if objDepth >= 0:
|
|
||||||
let objDisplay = genDisplay(m, t, objDepth)
|
|
||||||
let objDisplayStore = getTempName(m)
|
|
||||||
m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay])
|
|
||||||
addf(typeEntry, ", .display = $1", [rope(objDisplayStore)])
|
|
||||||
if isDefined(m.config, "nimTypeNames"):
|
|
||||||
var typeName: Rope
|
|
||||||
if t.kind in {tyObject, tyDistinct}:
|
|
||||||
typeName = genTypeInfo2Name(m, t)
|
|
||||||
else:
|
|
||||||
typeName = rope("NIM_NIL")
|
|
||||||
addf(typeEntry, ", .name = $1", [typeName])
|
|
||||||
add(typeEntry, ", .traceImpl = (void*)")
|
|
||||||
genHook(m, t, info, attachedTrace, typeEntry)
|
|
||||||
|
|
||||||
addf(typeEntry, ", .flags = $1};$n", [rope(flags)])
|
|
||||||
m.s[cfsVars].add typeEntry
|
|
||||||
|
|
||||||
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
|
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
|
||||||
discard genTypeInfoV1(m, t, info)
|
discard genTypeInfoV1(m, t, info)
|
||||||
|
|
@ -1435,14 +1347,14 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
|
|
||||||
let prefixTI = if m.hcrOn: "(" else: "(&"
|
let prefixTI = if m.hcrOn: "(" else: "(&"
|
||||||
|
|
||||||
let sig = hashType(origType, m.config)
|
let sig = hashType(origType)
|
||||||
result = m.typeInfoMarkerV2.getOrDefault(sig)
|
result = m.typeInfoMarkerV2.getOrDefault(sig)
|
||||||
if result != "":
|
if result != nil:
|
||||||
return prefixTI.rope & result & ")".rope
|
return prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
let marker = m.g.typeInfoMarkerV2.getOrDefault(sig)
|
let marker = m.g.typeInfoMarkerV2.getOrDefault(sig)
|
||||||
if marker.str != "":
|
if marker.str != nil:
|
||||||
cgsym(m, "TNimTypeV2")
|
discard cgsym(m, "TNimTypeV2")
|
||||||
declareNimType(m, "TNimTypeV2", marker.str, marker.owner)
|
declareNimType(m, "TNimTypeV2", marker.str, marker.owner)
|
||||||
# also store in local type section:
|
# also store in local type section:
|
||||||
m.typeInfoMarkerV2[sig] = marker.str
|
m.typeInfoMarkerV2[sig] = marker.str
|
||||||
|
|
@ -1456,15 +1368,12 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
# make sure the type info is created in the owner module
|
# make sure the type info is created in the owner module
|
||||||
discard genTypeInfoV2(m.g.modules[owner], origType, info)
|
discard genTypeInfoV2(m.g.modules[owner], origType, info)
|
||||||
# reference the type info as extern here
|
# reference the type info as extern here
|
||||||
cgsym(m, "TNimTypeV2")
|
discard cgsym(m, "TNimTypeV2")
|
||||||
declareNimType(m, "TNimTypeV2", result, owner)
|
declareNimType(m, "TNimTypeV2", result, owner)
|
||||||
return prefixTI.rope & result & ")".rope
|
return prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner)
|
m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner)
|
||||||
if m.compileToCpp:
|
genTypeInfoV2Impl(m, t, origType, result, info)
|
||||||
genTypeInfoV2OldImpl(m, t, origType, result, info)
|
|
||||||
else:
|
|
||||||
genTypeInfoV2Impl(m, t, origType, result, info)
|
|
||||||
result = prefixTI.rope & result & ")".rope
|
result = prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
proc openArrayToTuple(m: BModule; t: PType): PType =
|
proc openArrayToTuple(m: BModule; t: PType): PType =
|
||||||
|
|
@ -1509,15 +1418,15 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
|
|
||||||
let prefixTI = if m.hcrOn: "(" else: "(&"
|
let prefixTI = if m.hcrOn: "(" else: "(&"
|
||||||
|
|
||||||
let sig = hashType(origType, m.config)
|
let sig = hashType(origType)
|
||||||
result = m.typeInfoMarker.getOrDefault(sig)
|
result = m.typeInfoMarker.getOrDefault(sig)
|
||||||
if result != "":
|
if result != nil:
|
||||||
return prefixTI.rope & result & ")".rope
|
return prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
let marker = m.g.typeInfoMarker.getOrDefault(sig)
|
let marker = m.g.typeInfoMarker.getOrDefault(sig)
|
||||||
if marker.str != "":
|
if marker.str != nil:
|
||||||
cgsym(m, "TNimType")
|
discard cgsym(m, "TNimType")
|
||||||
cgsym(m, "TNimNode")
|
discard cgsym(m, "TNimNode")
|
||||||
declareNimType(m, "TNimType", marker.str, marker.owner)
|
declareNimType(m, "TNimType", marker.str, marker.owner)
|
||||||
# also store in local type section:
|
# also store in local type section:
|
||||||
m.typeInfoMarker[sig] = marker.str
|
m.typeInfoMarker[sig] = marker.str
|
||||||
|
|
@ -1528,8 +1437,8 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
|
|
||||||
let old = m.g.graph.emittedTypeInfo.getOrDefault($result)
|
let old = m.g.graph.emittedTypeInfo.getOrDefault($result)
|
||||||
if old != FileIndex(0):
|
if old != FileIndex(0):
|
||||||
cgsym(m, "TNimType")
|
discard cgsym(m, "TNimType")
|
||||||
cgsym(m, "TNimNode")
|
discard cgsym(m, "TNimNode")
|
||||||
declareNimType(m, "TNimType", result, old.int)
|
declareNimType(m, "TNimType", result, old.int)
|
||||||
return prefixTI.rope & result & ")".rope
|
return prefixTI.rope & result & ")".rope
|
||||||
|
|
||||||
|
|
@ -1538,8 +1447,8 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
# make sure the type info is created in the owner module
|
# make sure the type info is created in the owner module
|
||||||
discard genTypeInfoV1(m.g.modules[owner], origType, info)
|
discard genTypeInfoV1(m.g.modules[owner], origType, info)
|
||||||
# reference the type info as extern here
|
# reference the type info as extern here
|
||||||
cgsym(m, "TNimType")
|
discard cgsym(m, "TNimType")
|
||||||
cgsym(m, "TNimNode")
|
discard cgsym(m, "TNimNode")
|
||||||
declareNimType(m, "TNimType", result, owner)
|
declareNimType(m, "TNimType", result, owner)
|
||||||
return prefixTI.rope & result & ")".rope
|
return prefixTI.rope & result & ")".rope
|
||||||
else:
|
else:
|
||||||
|
|
@ -1566,12 +1475,12 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
genTupleInfo(m, x, x, result, info)
|
genTupleInfo(m, x, x, result, info)
|
||||||
of tySequence:
|
of tySequence:
|
||||||
genTypeInfoAux(m, t, t, result, info)
|
genTypeInfoAux(m, t, t, result, info)
|
||||||
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
|
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}:
|
||||||
let markerProc = genTraverseProc(m, origType, sig)
|
let markerProc = genTraverseProc(m, origType, sig)
|
||||||
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||||
of tyRef:
|
of tyRef:
|
||||||
genTypeInfoAux(m, t, t, result, info)
|
genTypeInfoAux(m, t, t, result, info)
|
||||||
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}:
|
if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}:
|
||||||
let markerProc = genTraverseProc(m, origType, sig)
|
let markerProc = genTraverseProc(m, origType, sig)
|
||||||
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||||
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
||||||
|
|
@ -1606,9 +1515,3 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||||
|
|
||||||
proc genTypeSection(m: BModule, n: PNode) =
|
proc genTypeSection(m: BModule, n: PNode) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc genTypeInfo*(config: ConfigRef, m: BModule, t: PType; info: TLineInfo): Rope =
|
|
||||||
if optTinyRtti in config.globalOptions:
|
|
||||||
result = genTypeInfoV2(m, t, info)
|
|
||||||
else:
|
|
||||||
result = genTypeInfoV1(m, t, info)
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ import
|
||||||
ast, types, hashes, strutils, msgs, wordrecg,
|
ast, types, hashes, strutils, msgs, wordrecg,
|
||||||
platform, trees, options, cgendata
|
platform, trees, options, cgendata
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkStmtList:
|
of nkStmtList:
|
||||||
|
|
@ -53,7 +50,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt =
|
||||||
a = a + (a shl 3)
|
a = a + (a shl 3)
|
||||||
a = a xor (a shr 11)
|
a = a xor (a shr 11)
|
||||||
a = a + (a shl 15)
|
a = a + (a shl 15)
|
||||||
result = cast[Hash](uint(a))
|
result = cast[Hash](a)
|
||||||
|
|
||||||
template getUniqueType*(key: PType): PType = key
|
template getUniqueType*(key: PType): PType = key
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -16,6 +16,7 @@ import
|
||||||
type
|
type
|
||||||
TLabel* = Rope # for the C generator a label is just a rope
|
TLabel* = Rope # for the C generator a label is just a rope
|
||||||
TCFileSection* = enum # the sections a generated C file consists of
|
TCFileSection* = enum # the sections a generated C file consists of
|
||||||
|
cfsMergeInfo, # section containing merge information
|
||||||
cfsHeaders, # section for C include file headers
|
cfsHeaders, # section for C include file headers
|
||||||
cfsFrameDefines # section for nim frame macros
|
cfsFrameDefines # section for nim frame macros
|
||||||
cfsForwardTypes, # section for C forward typedefs
|
cfsForwardTypes, # section for C forward typedefs
|
||||||
|
|
@ -23,17 +24,21 @@ type
|
||||||
cfsSeqTypes, # section for sequence types only
|
cfsSeqTypes, # section for sequence types only
|
||||||
# this is needed for strange type generation
|
# this is needed for strange type generation
|
||||||
# reasons
|
# reasons
|
||||||
|
cfsFieldInfo, # section for field information
|
||||||
cfsTypeInfo, # section for type information (ag ABI checks)
|
cfsTypeInfo, # section for type information (ag ABI checks)
|
||||||
cfsProcHeaders, # section for C procs prototypes
|
cfsProcHeaders, # section for C procs prototypes
|
||||||
cfsStrData, # section for constant string literals
|
|
||||||
cfsData, # section for C constant data
|
cfsData, # section for C constant data
|
||||||
cfsVars, # section for C variable declarations
|
cfsVars, # section for C variable declarations
|
||||||
cfsProcs, # section for C procs that are not inline
|
cfsProcs, # section for C procs that are not inline
|
||||||
cfsInitProc, # section for the C init proc
|
cfsInitProc, # section for the C init proc
|
||||||
cfsDatInitProc, # section for the C datInit proc
|
cfsDatInitProc, # section for the C datInit proc
|
||||||
cfsTypeInit1, # section 1 for declarations of type information
|
cfsTypeInit1, # section 1 for declarations of type information
|
||||||
|
cfsTypeInit2, # section 2 for init of type information
|
||||||
cfsTypeInit3, # section 3 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
|
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
|
TCTypeKind* = enum # describes the type kind of a C type
|
||||||
ctVoid, ctChar, ctBool,
|
ctVoid, ctChar, ctBool,
|
||||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
||||||
|
|
@ -86,7 +91,6 @@ type
|
||||||
options*: TOptions # options that should be used for code
|
options*: TOptions # options that should be used for code
|
||||||
# generation; this is the same as prc.options
|
# generation; this is the same as prc.options
|
||||||
# unless prc == nil
|
# unless prc == nil
|
||||||
optionsStack*: seq[TOptions]
|
|
||||||
module*: BModule # used to prevent excessive parameter passing
|
module*: BModule # used to prevent excessive parameter passing
|
||||||
withinLoop*: int # > 0 if we are within a loop
|
withinLoop*: int # > 0 if we are within a loop
|
||||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||||
|
|
@ -95,7 +99,6 @@ type
|
||||||
withinTryWithExcept*: int # required for goto based exception handling
|
withinTryWithExcept*: int # required for goto based exception handling
|
||||||
withinBlockLeaveActions*: int # complex to explain
|
withinBlockLeaveActions*: int # complex to explain
|
||||||
sigConflicts*: CountTable[string]
|
sigConflicts*: CountTable[string]
|
||||||
inUncheckedAssignSection*: int
|
|
||||||
|
|
||||||
TTypeSeq* = seq[PType]
|
TTypeSeq* = seq[PType]
|
||||||
TypeCache* = Table[SigHash, Rope]
|
TypeCache* = Table[SigHash, Rope]
|
||||||
|
|
@ -167,13 +170,13 @@ type
|
||||||
labels*: Natural # for generating unique module-scope names
|
labels*: Natural # for generating unique module-scope names
|
||||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||||
# OpenGL wrapper
|
# OpenGL wrapper
|
||||||
|
injectStmt*: Rope
|
||||||
sigConflicts*: CountTable[SigHash]
|
sigConflicts*: CountTable[SigHash]
|
||||||
g*: BModuleList
|
g*: BModuleList
|
||||||
ndi*: NdiFile
|
ndi*: NdiFile
|
||||||
|
|
||||||
template config*(m: BModule): ConfigRef = m.g.config
|
template config*(m: BModule): ConfigRef = m.g.config
|
||||||
template config*(p: BProc): ConfigRef = p.module.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) =
|
proc includeHeader*(this: BModule; header: string) =
|
||||||
if not this.headerFiles.contains header:
|
if not this.headerFiles.contains header:
|
||||||
|
|
@ -187,18 +190,13 @@ proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||||
# top level proc sections
|
# top level proc sections
|
||||||
result = p.blocks[0].sections[s]
|
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 =
|
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||||
new(result)
|
new(result)
|
||||||
result.prc = prc
|
result.prc = prc
|
||||||
result.module = module
|
result.module = module
|
||||||
result.options = if prc != nil: prc.options
|
result.options = if prc != nil: prc.options
|
||||||
else: module.config.options
|
else: module.config.options
|
||||||
result.blocks = @[initBlock()]
|
newSeq(result.blocks, 1)
|
||||||
result.nestedTryStmts = @[]
|
result.nestedTryStmts = @[]
|
||||||
result.finallySafePoints = @[]
|
result.finallySafePoints = @[]
|
||||||
result.sigConflicts = initCountTable[string]()
|
result.sigConflicts = initCountTable[string]()
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, options, ast, msgs, idents, renderer, types, magicsys,
|
intsets, options, ast, msgs, idents, renderer, types, magicsys,
|
||||||
sempass2, modulegraphs, lineinfos
|
sempass2, strutils, modulegraphs, lineinfos
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
|
|
||||||
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
||||||
var dest = skipTypes(d, abstractPtrs)
|
var dest = skipTypes(d, abstractPtrs)
|
||||||
|
|
@ -50,7 +46,6 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||||
# replace ordinary method by dispatcher method:
|
# replace ordinary method by dispatcher method:
|
||||||
let disp = getDispatcher(result[0].sym)
|
let disp = getDispatcher(result[0].sym)
|
||||||
if disp != nil:
|
if disp != nil:
|
||||||
result[0].typ = disp.typ
|
|
||||||
result[0].sym = disp
|
result[0].sym = disp
|
||||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||||
for i in 1..<result.len:
|
for i in 1..<result.len:
|
||||||
|
|
@ -124,7 +119,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
||||||
disp.ast = copyTree(s.ast)
|
disp.ast = copyTree(s.ast)
|
||||||
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||||
disp.loc.r = ""
|
disp.loc.r = nil
|
||||||
if s.typ[0] != nil:
|
if s.typ[0] != nil:
|
||||||
if disp.ast.len > resultPos:
|
if disp.ast.len > resultPos:
|
||||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
||||||
|
|
@ -148,6 +143,23 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||||
disp.ast[resultPos].kind == nkEmpty:
|
disp.ast[resultPos].kind == nkEmpty:
|
||||||
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
|
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||||
var witness: PSym
|
var witness: PSym
|
||||||
for i in 0..<g.methods.len:
|
for i in 0..<g.methods.len:
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,7 @@
|
||||||
# yield 2
|
# yield 2
|
||||||
# if :unrollFinally: # This node is created by `newEndFinallyNode`
|
# if :unrollFinally: # This node is created by `newEndFinallyNode`
|
||||||
# if :curExc.isNil:
|
# if :curExc.isNil:
|
||||||
# if nearestFinally == 0:
|
# return :tmpResult
|
||||||
# return :tmpResult
|
|
||||||
# else:
|
|
||||||
# :state = nearestFinally # bubble up
|
|
||||||
# else:
|
# else:
|
||||||
# closureIterSetupExc(nil)
|
# closureIterSetupExc(nil)
|
||||||
# raise
|
# raise
|
||||||
|
|
@ -136,9 +133,6 @@ import
|
||||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
|
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
|
||||||
tables, options
|
tables, options
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Ctx = object
|
Ctx = object
|
||||||
g: ModuleGraph
|
g: ModuleGraph
|
||||||
|
|
@ -420,11 +414,8 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
|
||||||
|
|
||||||
|
|
||||||
proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
|
proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
|
||||||
if isEmptyType(v.typ):
|
result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
|
||||||
result = v
|
result.info = v.info
|
||||||
else:
|
|
||||||
result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v)
|
|
||||||
result.info = v.info
|
|
||||||
|
|
||||||
proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
|
proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
|
||||||
if input.kind == nkStmtListExpr:
|
if input.kind == nkStmtListExpr:
|
||||||
|
|
@ -436,7 +427,8 @@ proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
|
||||||
|
|
||||||
proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
|
proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
|
||||||
result = newNodeI(nkStmtList, exprBody.info)
|
result = newNodeI(nkStmtList, exprBody.info)
|
||||||
ctx.addExprAssgn(result, exprBody, res)
|
if exprBody.typ != nil:
|
||||||
|
ctx.addExprAssgn(result, exprBody, res)
|
||||||
|
|
||||||
proc newNotCall(g: ModuleGraph; e: PNode): PNode =
|
proc newNotCall(g: ModuleGraph; e: PNode): PNode =
|
||||||
result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
|
result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
|
||||||
|
|
@ -614,12 +606,6 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||||
internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
|
internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
|
||||||
result.add(n)
|
result.add(n)
|
||||||
result.add(ctx.newEnvVarAccess(tmp))
|
result.add(ctx.newEnvVarAccess(tmp))
|
||||||
elif n[0].kind == nkStmtListExpr:
|
|
||||||
result = newNodeI(nkStmtList, n.info)
|
|
||||||
let (st, ex) = exprToStmtList(n[0])
|
|
||||||
result.add(st)
|
|
||||||
n[0] = ex
|
|
||||||
result.add(n)
|
|
||||||
|
|
||||||
of nkCallKinds, nkChckRange, nkChckRangeF, nkChckRange64:
|
of nkCallKinds, nkChckRange, nkChckRangeF, nkChckRange64:
|
||||||
var ns = false
|
var ns = false
|
||||||
|
|
@ -718,7 +704,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||||
n[^1] = ex
|
n[^1] = ex
|
||||||
result.add(n)
|
result.add(n)
|
||||||
|
|
||||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
var ns = false
|
var ns = false
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||||
|
|
@ -810,10 +796,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||||
# Generate the following code:
|
# Generate the following code:
|
||||||
# if :unrollFinally:
|
# if :unrollFinally:
|
||||||
# if :curExc.isNil:
|
# if :curExc.isNil:
|
||||||
# if nearestFinally == 0:
|
# return :tmpResult
|
||||||
# return :tmpResult
|
|
||||||
# else:
|
|
||||||
# :state = nearestFinally # bubble up
|
|
||||||
# else:
|
# else:
|
||||||
# raise
|
# raise
|
||||||
let curExc = ctx.newCurExcAccess()
|
let curExc = ctx.newCurExcAccess()
|
||||||
|
|
@ -822,20 +805,11 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||||
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
|
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
|
||||||
cmp.typ = ctx.g.getSysType(info, tyBool)
|
cmp.typ = ctx.g.getSysType(info, tyBool)
|
||||||
|
|
||||||
let retStmt =
|
let asgn = newTree(nkFastAsgn,
|
||||||
if ctx.nearestFinally == 0:
|
newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
|
||||||
# last finally, we can return
|
ctx.newTmpResultAccess())
|
||||||
let retValue = if ctx.fn.typ[0].isNil:
|
|
||||||
ctx.g.emptyNode
|
|
||||||
else:
|
|
||||||
newTree(nkFastAsgn,
|
|
||||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
|
|
||||||
ctx.newTmpResultAccess())
|
|
||||||
newTree(nkReturnStmt, retValue)
|
|
||||||
else:
|
|
||||||
# bubble up to next finally
|
|
||||||
newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally))
|
|
||||||
|
|
||||||
|
let retStmt = newTree(nkReturnStmt, asgn)
|
||||||
let branch = newTree(nkElifBranch, cmp, retStmt)
|
let branch = newTree(nkElifBranch, cmp, retStmt)
|
||||||
|
|
||||||
let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
|
let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
|
||||||
|
|
@ -868,7 +842,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||||
if n[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
let asgnTmpResult = newNodeI(nkAsgn, n.info)
|
let asgnTmpResult = newNodeI(nkAsgn, n.info)
|
||||||
asgnTmpResult.add(ctx.newTmpResultAccess())
|
asgnTmpResult.add(ctx.newTmpResultAccess())
|
||||||
let x = if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: n[0][1] else: n[0]
|
let x = if n[0].kind in {nkAsgn, nkFastAsgn}: n[0][1] else: n[0]
|
||||||
asgnTmpResult.add(x)
|
asgnTmpResult.add(x)
|
||||||
result.add(asgnTmpResult)
|
result.add(asgnTmpResult)
|
||||||
|
|
||||||
|
|
@ -879,13 +853,6 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||||
|
|
||||||
of nkSkip:
|
of nkSkip:
|
||||||
discard
|
discard
|
||||||
of nkTryStmt:
|
|
||||||
if n.hasYields:
|
|
||||||
# the inner try will handle these transformations
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
n[i] = ctx.transformReturnsInTry(n[i])
|
|
||||||
else:
|
else:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
n[i] = ctx.transformReturnsInTry(n[i])
|
n[i] = ctx.transformReturnsInTry(n[i])
|
||||||
|
|
@ -1155,7 +1122,7 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: P
|
||||||
result = newType(tyArray, nextTypeId(idgen), owner)
|
result = newType(tyArray, nextTypeId(idgen), owner)
|
||||||
|
|
||||||
let rng = newType(tyRange, nextTypeId(idgen), owner)
|
let rng = newType(tyRange, nextTypeId(idgen), owner)
|
||||||
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n - 1))
|
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
|
||||||
rng.rawAddSon(t)
|
rng.rawAddSon(t)
|
||||||
|
|
||||||
result.rawAddSon(rng)
|
result.rawAddSon(rng)
|
||||||
|
|
@ -1372,24 +1339,20 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||||
# detect: 'finally: raises X' which is currently not supported. We produce
|
# detect: 'finally: raises X' which is currently not supported. We produce
|
||||||
# an error for this case for now. All this will be done properly with Yuriy's
|
# an error for this case for now. All this will be done properly with Yuriy's
|
||||||
# patch.
|
# patch.
|
||||||
|
|
||||||
result = n
|
result = n
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkTryStmt:
|
of nkTryStmt:
|
||||||
let f = n.lastSon
|
let f = n.lastSon
|
||||||
var didAddSomething = false
|
|
||||||
if f.kind == nkFinally:
|
if f.kind == nkFinally:
|
||||||
c.finallys.add f.lastSon
|
c.finallys.add f.lastSon
|
||||||
didAddSomething = true
|
|
||||||
|
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result[i] = preprocess(c, n[i])
|
result[i] = preprocess(c, n[i])
|
||||||
|
|
||||||
if didAddSomething:
|
if f.kind == nkFinally:
|
||||||
discard c.finallys.pop()
|
discard c.finallys.pop()
|
||||||
|
|
||||||
of nkWhileStmt, nkBlockStmt:
|
of nkWhileStmt, nkBlockStmt:
|
||||||
if n.hasYields == false: return n
|
|
||||||
c.blocks.add((n, c.finallys.len))
|
c.blocks.add((n, c.finallys.len))
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result[i] = preprocess(c, n[i])
|
result[i] = preprocess(c, n[i])
|
||||||
|
|
@ -1413,7 +1376,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
for i in countdown(c.finallys.high, fin):
|
for i in countdown(c.finallys.high, fin):
|
||||||
var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
|
var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
|
||||||
result.add freshVars(copyTree(c.finallys[i]), vars)
|
result.add freshVars(preprocess(c, c.finallys[i]), vars)
|
||||||
c.idgen = vars.idgen
|
c.idgen = vars.idgen
|
||||||
result.add n
|
result.add n
|
||||||
of nkSkip: discard
|
of nkSkip: discard
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,14 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
|
||||||
elif self.supportsStdinFile and conf.projectName == "-":
|
elif self.supportsStdinFile and conf.projectName == "-":
|
||||||
handleStdinInput(conf)
|
handleStdinInput(conf)
|
||||||
elif conf.projectName != "":
|
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:
|
else:
|
||||||
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
|
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
|
||||||
|
|
||||||
|
|
@ -64,8 +71,7 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
|
||||||
if conf.cmd == cmdNimscript: return false
|
if conf.cmd == cmdNimscript: return false
|
||||||
# now process command line arguments again, because some options in the
|
# now process command line arguments again, because some options in the
|
||||||
# command line can overwrite the config file's settings
|
# command line can overwrite the config file's settings
|
||||||
if conf.backend != backendJs: # bug #19059
|
extccomp.initVars(conf)
|
||||||
extccomp.initVars(conf)
|
|
||||||
self.processCmdLine(passCmd2, "", conf)
|
self.processCmdLine(passCmd2, "", conf)
|
||||||
if conf.cmd == cmdNone:
|
if conf.cmd == cmdNone:
|
||||||
rawMessage(conf, errGenerated, "command missing")
|
rawMessage(conf, errGenerated, "command missing")
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
# This module handles the parsing of command line arguments.
|
# This module handles the parsing of command line arguments.
|
||||||
|
|
||||||
|
|
||||||
# We do this here before the 'import' statement so 'defined' does not get
|
# We do this here before the 'import' statement so 'defined' does not get
|
||||||
# confused with 'TGCMode.gcMarkAndSweep' etc.
|
# confused with 'TGCMode.gcMarkAndSweep' etc.
|
||||||
template bootSwitch(name, expr, userString) =
|
template bootSwitch(name, expr, userString) =
|
||||||
|
|
@ -24,15 +25,12 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
|
||||||
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
|
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
|
||||||
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
|
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
|
||||||
|
|
||||||
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
|
|
||||||
import
|
import
|
||||||
msgs, options, nversion, condsyms, extccomp, platform,
|
os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
|
||||||
wordrecg, nimblecmd, lineinfos, pathutils, pathnorm
|
wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
|
||||||
|
pathutils, strtabs, pathnorm
|
||||||
|
|
||||||
from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
|
from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
# but some have deps to imported modules. Yay.
|
# but some have deps to imported modules. Yay.
|
||||||
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
||||||
|
|
@ -116,7 +114,7 @@ const
|
||||||
errInvalidCmdLineOption = "invalid command line option: '$1'"
|
errInvalidCmdLineOption = "invalid command line option: '$1'"
|
||||||
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
|
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
|
||||||
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
|
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
|
||||||
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
|
errOffHintsError = "'off', 'hint' or 'error' expected, but '$1' found"
|
||||||
|
|
||||||
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
|
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
|
||||||
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
|
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
|
||||||
|
|
@ -184,7 +182,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||||
info: TLineInfo; orig: string; conf: ConfigRef) =
|
info: TLineInfo; orig: string; conf: ConfigRef) =
|
||||||
var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off
|
var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off
|
||||||
var i = 0
|
var i = 0
|
||||||
var notes: set[TMsgKind]
|
var n = hintMin
|
||||||
var isBracket = false
|
var isBracket = false
|
||||||
if i < arg.len and arg[i] == '[':
|
if i < arg.len and arg[i] == '[':
|
||||||
isBracket = true
|
isBracket = true
|
||||||
|
|
@ -199,38 +197,37 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||||
if i == arg.len: discard
|
if i == arg.len: discard
|
||||||
elif i < arg.len and (arg[i] in {':', '='}): inc(i)
|
elif i < arg.len and (arg[i] in {':', '='}): inc(i)
|
||||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||||
|
# unfortunately, hintUser and warningUser clash
|
||||||
|
if state in {wHint, wHintAsError}:
|
||||||
|
let x = findStr(hintMin, hintMax, id, errUnknown)
|
||||||
|
if x != errUnknown: n = TNoteKind(x)
|
||||||
|
else: localError(conf, info, "unknown hint: " & id)
|
||||||
|
else:
|
||||||
|
let x = findStr(warnMin, warnMax, id, errUnknown)
|
||||||
|
if x != errUnknown: n = TNoteKind(x)
|
||||||
|
else: localError(conf, info, "unknown warning: " & id)
|
||||||
|
|
||||||
let isSomeHint = state in {wHint, wHintAsError}
|
|
||||||
template findNote(noteMin, noteMax, name) =
|
|
||||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
|
||||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
|
||||||
if x != errUnknown: notes = {TNoteKind(x)}
|
|
||||||
else: localError(conf, info, "unknown $#: $#" % [name, id])
|
|
||||||
case id.normalize
|
|
||||||
of "all": # other note groups would be easy to support via additional cases
|
|
||||||
notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
|
|
||||||
elif isSomeHint: findNote(hintMin, hintMax, "hint")
|
|
||||||
else: findNote(warnMin, warnMax, "warning")
|
|
||||||
var val = substr(arg, i).normalize
|
var val = substr(arg, i).normalize
|
||||||
if val == "": val = "on"
|
if val == "": val = "on"
|
||||||
if val notin ["on", "off"]:
|
if val notin ["on", "off"]:
|
||||||
# xxx in future work we should also allow users to have control over `foreignPackageNotes`
|
|
||||||
# so that they can enable `hints|warnings|warningAsErrors` for all the code they depend on.
|
|
||||||
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||||
else:
|
elif n notin conf.cmdlineNotes or pass == passCmd1:
|
||||||
let isOn = val == "on"
|
if pass == passCmd1: incl(conf.cmdlineNotes, n)
|
||||||
if isOn and id.normalize == "all":
|
incl(conf.modifiedyNotes, n)
|
||||||
localError(conf, info, "only 'all:off' is supported")
|
case val
|
||||||
for n in notes:
|
of "on":
|
||||||
if n notin conf.cmdlineNotes or pass == passCmd1:
|
if state in {wWarningAsError, wHintAsError}:
|
||||||
if pass == passCmd1: incl(conf.cmdlineNotes, n)
|
incl(conf.warningAsErrors, n) # xxx rename warningAsErrors to noteAsErrors
|
||||||
incl(conf.modifiedyNotes, n)
|
else:
|
||||||
if state in {wWarningAsError, wHintAsError}:
|
incl(conf.notes, n)
|
||||||
conf.warningAsErrors[n] = isOn # xxx rename warningAsErrors to noteAsErrors
|
incl(conf.mainPackageNotes, n)
|
||||||
else:
|
of "off":
|
||||||
conf.notes[n] = isOn
|
if state in {wWarningAsError, wHintAsError}:
|
||||||
conf.mainPackageNotes[n] = isOn
|
excl(conf.warningAsErrors, n)
|
||||||
if not isOn: excl(conf.foreignPackageNotes, n)
|
else:
|
||||||
|
excl(conf.notes, n)
|
||||||
|
excl(conf.mainPackageNotes, n)
|
||||||
|
excl(conf.foreignPackageNotes, n)
|
||||||
|
|
||||||
proc processCompile(conf: ConfigRef; filename: string) =
|
proc processCompile(conf: ConfigRef; filename: string) =
|
||||||
var found = findFile(conf, filename)
|
var found = findFile(conf, filename)
|
||||||
|
|
@ -241,7 +238,7 @@ const
|
||||||
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
||||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found"
|
||||||
|
|
||||||
template warningOptionNoop(switch: string) =
|
template warningOptionNoop(switch: string) =
|
||||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||||
|
|
@ -251,7 +248,7 @@ template deprecatedAlias(oldName, newName: string) =
|
||||||
|
|
||||||
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
||||||
case switch.normalize
|
case switch.normalize
|
||||||
of "gc", "mm":
|
of "gc":
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "boehm": result = conf.selectedGC == gcBoehm
|
of "boehm": result = conf.selectedGC == gcBoehm
|
||||||
of "refc": result = conf.selectedGC == gcRefc
|
of "refc": result = conf.selectedGC == gcRefc
|
||||||
|
|
@ -262,6 +259,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||||
of "go": result = conf.selectedGC == gcGo
|
of "go": result = conf.selectedGC == gcGo
|
||||||
of "none": result = conf.selectedGC == gcNone
|
of "none": result = conf.selectedGC == gcNone
|
||||||
of "stack", "regions": result = conf.selectedGC == gcRegions
|
of "stack", "regions": result = conf.selectedGC == gcRegions
|
||||||
|
of "v2", "generational": warningOptionNoop(arg)
|
||||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||||
of "opt":
|
of "opt":
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
|
|
@ -326,7 +324,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||||
of "run", "r": result = contains(conf.globalOptions, optRun)
|
of "run", "r": result = contains(conf.globalOptions, optRun)
|
||||||
of "symbolfiles": result = conf.symbolFiles != disabledSf
|
of "symbolfiles": result = conf.symbolFiles != disabledSf
|
||||||
of "genscript": result = contains(conf.globalOptions, optGenScript)
|
of "genscript": result = contains(conf.globalOptions, optGenScript)
|
||||||
of "gencdeps": result = contains(conf.globalOptions, optGenCDeps)
|
|
||||||
of "threads": result = contains(conf.globalOptions, optThreads)
|
of "threads": result = contains(conf.globalOptions, optThreads)
|
||||||
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
||||||
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
|
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
|
||||||
|
|
@ -335,7 +332,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||||
result = contains(conf.options, optTrMacros)
|
result = contains(conf.options, optTrMacros)
|
||||||
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||||
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
|
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
|
||||||
of "panics": result = contains(conf.globalOptions, optPanics)
|
|
||||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||||
|
|
||||||
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
||||||
|
|
@ -447,11 +443,9 @@ proc parseCommand*(command: string): Command =
|
||||||
of "check": cmdCheck
|
of "check": cmdCheck
|
||||||
of "e": cmdNimscript
|
of "e": cmdNimscript
|
||||||
of "doc0": cmdDoc0
|
of "doc0": cmdDoc0
|
||||||
of "doc2", "doc": cmdDoc
|
of "doc2", "doc": cmdDoc2
|
||||||
of "doc2tex": cmdDoc2tex
|
of "doc2tex": cmdDoc2tex
|
||||||
of "rst2html": cmdRst2html
|
of "rst2html": cmdRst2html
|
||||||
of "md2tex": cmdMd2tex
|
|
||||||
of "md2html": cmdMd2html
|
|
||||||
of "rst2tex": cmdRst2tex
|
of "rst2tex": cmdRst2tex
|
||||||
of "jsondoc0": cmdJsondoc0
|
of "jsondoc0": cmdJsondoc0
|
||||||
of "jsondoc2", "jsondoc": cmdJsondoc
|
of "jsondoc2", "jsondoc": cmdJsondoc
|
||||||
|
|
@ -483,108 +477,11 @@ proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||||
# command early customizations
|
# command early customizations
|
||||||
# must be handled here to honor subsequent `--hint:x:on|off`
|
# must be handled here to honor subsequent `--hint:x:on|off`
|
||||||
case conf.cmd
|
case conf.cmd
|
||||||
of cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex:
|
of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc.
|
||||||
# xxx see whether to add others: cmdGendepend, etc.
|
|
||||||
conf.foreignPackageNotes = {hintSuccessX}
|
conf.foreignPackageNotes = {hintSuccessX}
|
||||||
else:
|
else:
|
||||||
conf.foreignPackageNotes = foreignPackageNotesDefault
|
conf.foreignPackageNotes = foreignPackageNotesDefault
|
||||||
|
|
||||||
proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) =
|
|
||||||
# Keep this syncronized with the default config/nim.cfg!
|
|
||||||
if cmpIgnoreStyle(key, "nimQuirky") == 0:
|
|
||||||
conf.exc = excQuirky
|
|
||||||
elif cmpIgnoreStyle(key, "release") == 0 or cmpIgnoreStyle(key, "danger") == 0:
|
|
||||||
if pass in {passCmd1, passPP}:
|
|
||||||
conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize}
|
|
||||||
conf.globalOptions.excl {optExcessiveStackTrace, optCDebug}
|
|
||||||
conf.options.incl optOptimizeSpeed
|
|
||||||
if cmpIgnoreStyle(key, "danger") == 0 or cmpIgnoreStyle(key, "quick") == 0:
|
|
||||||
if pass in {passCmd1, passPP}:
|
|
||||||
conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
|
|
||||||
optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir}
|
|
||||||
conf.globalOptions.excl {optCDebug}
|
|
||||||
|
|
||||||
proc initOrcDefines*(conf: ConfigRef) =
|
|
||||||
conf.selectedGC = gcOrc
|
|
||||||
defineSymbol(conf.symbols, "gcorc")
|
|
||||||
defineSymbol(conf.symbols, "gcdestructors")
|
|
||||||
incl conf.globalOptions, optSeqDestructors
|
|
||||||
incl conf.globalOptions, optTinyRtti
|
|
||||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
|
||||||
defineSymbol(conf.symbols, "nimV2")
|
|
||||||
if conf.exc == excNone and conf.backend != backendCpp:
|
|
||||||
conf.exc = excGoto
|
|
||||||
|
|
||||||
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
|
|
||||||
if isOrc:
|
|
||||||
conf.selectedGC = gcOrc
|
|
||||||
defineSymbol(conf.symbols, "gcorc")
|
|
||||||
else:
|
|
||||||
conf.selectedGC = gcArc
|
|
||||||
defineSymbol(conf.symbols, "gcarc")
|
|
||||||
|
|
||||||
defineSymbol(conf.symbols, "gcdestructors")
|
|
||||||
incl conf.globalOptions, optSeqDestructors
|
|
||||||
incl conf.globalOptions, optTinyRtti
|
|
||||||
if pass in {passCmd2, passPP}:
|
|
||||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
|
||||||
defineSymbol(conf.symbols, "nimV2")
|
|
||||||
if conf.exc == excNone and conf.backend != backendCpp:
|
|
||||||
conf.exc = excGoto
|
|
||||||
|
|
||||||
proc unregisterArcOrc(conf: ConfigRef) =
|
|
||||||
undefSymbol(conf.symbols, "gcdestructors")
|
|
||||||
undefSymbol(conf.symbols, "gcarc")
|
|
||||||
undefSymbol(conf.symbols, "gcorc")
|
|
||||||
undefSymbol(conf.symbols, "nimSeqsV2")
|
|
||||||
undefSymbol(conf.symbols, "nimV2")
|
|
||||||
excl conf.globalOptions, optSeqDestructors
|
|
||||||
excl conf.globalOptions, optTinyRtti
|
|
||||||
|
|
||||||
proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
|
|
||||||
info: TLineInfo; conf: ConfigRef) =
|
|
||||||
if conf.backend == backendJs: return # for: bug #16033
|
|
||||||
expectArg(conf, switch, arg, pass, info)
|
|
||||||
if pass in {passCmd2, passPP}:
|
|
||||||
case arg.normalize
|
|
||||||
of "boehm":
|
|
||||||
unregisterArcOrc(conf)
|
|
||||||
conf.selectedGC = gcBoehm
|
|
||||||
defineSymbol(conf.symbols, "boehmgc")
|
|
||||||
incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
|
|
||||||
of "refc":
|
|
||||||
unregisterArcOrc(conf)
|
|
||||||
defineSymbol(conf.symbols, "gcrefc")
|
|
||||||
conf.selectedGC = gcRefc
|
|
||||||
of "markandsweep":
|
|
||||||
unregisterArcOrc(conf)
|
|
||||||
conf.selectedGC = gcMarkAndSweep
|
|
||||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
|
||||||
of "destructors", "arc":
|
|
||||||
registerArcOrc(pass, conf, false)
|
|
||||||
of "orc":
|
|
||||||
registerArcOrc(pass, conf, true)
|
|
||||||
of "hooks":
|
|
||||||
conf.selectedGC = gcHooks
|
|
||||||
defineSymbol(conf.symbols, "gchooks")
|
|
||||||
incl conf.globalOptions, optSeqDestructors
|
|
||||||
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
|
||||||
if pass in {passCmd2, passPP}:
|
|
||||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
|
||||||
of "go":
|
|
||||||
unregisterArcOrc(conf)
|
|
||||||
conf.selectedGC = gcGo
|
|
||||||
defineSymbol(conf.symbols, "gogc")
|
|
||||||
of "none":
|
|
||||||
unregisterArcOrc(conf)
|
|
||||||
conf.selectedGC = gcNone
|
|
||||||
defineSymbol(conf.symbols, "nogc")
|
|
||||||
of "stack", "regions":
|
|
||||||
unregisterArcOrc(conf)
|
|
||||||
conf.selectedGC = gcRegions
|
|
||||||
defineSymbol(conf.symbols, "gcregions")
|
|
||||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
|
||||||
|
|
||||||
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
conf: ConfigRef) =
|
conf: ConfigRef) =
|
||||||
var
|
var
|
||||||
|
|
@ -626,10 +523,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
conf.lazyPaths.keepItIf(it != path)
|
conf.lazyPaths.keepItIf(it != path)
|
||||||
of "nimcache":
|
of "nimcache":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
var arg = arg
|
|
||||||
# refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set
|
|
||||||
# in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")`
|
|
||||||
if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/')
|
|
||||||
conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true)
|
conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true)
|
||||||
of "out", "o":
|
of "out", "o":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
|
|
@ -649,18 +542,18 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "backend", "b":
|
of "backend", "b":
|
||||||
let backend = parseEnum(arg.normalize, TBackend.default)
|
let backend = parseEnum(arg.normalize, TBackend.default)
|
||||||
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
|
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
|
||||||
if backend == backendJs: # bug #21209
|
|
||||||
conf.globalOptions.excl {optThreadAnalysis, optThreads}
|
|
||||||
conf.backend = backend
|
conf.backend = backend
|
||||||
of "doccmd": conf.docCmd = arg
|
of "doccmd": conf.docCmd = arg
|
||||||
of "define", "d":
|
of "define", "d":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if {':', '='} in arg:
|
if {':', '='} in arg:
|
||||||
splitSwitch(conf, arg, key, val, pass, info)
|
splitSwitch(conf, arg, key, val, pass, info)
|
||||||
specialDefine(conf, key, pass)
|
if cmpIgnoreStyle(key, "nimQuirky") == 0:
|
||||||
|
conf.exc = excQuirky
|
||||||
defineSymbol(conf.symbols, key, val)
|
defineSymbol(conf.symbols, key, val)
|
||||||
else:
|
else:
|
||||||
specialDefine(conf, arg, pass)
|
if cmpIgnoreStyle(arg, "nimQuirky") == 0:
|
||||||
|
conf.exc = excQuirky
|
||||||
defineSymbol(conf.symbols, arg)
|
defineSymbol(conf.symbols, arg)
|
||||||
of "undef", "u":
|
of "undef", "u":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
|
|
@ -687,10 +580,59 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "project":
|
of "project":
|
||||||
processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
|
processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
|
||||||
of "gc":
|
of "gc":
|
||||||
warningDeprecated(conf, info, "`gc:option` is deprecated; use `mm:option` instead")
|
if conf.backend == backendJs: return # for: bug #16033
|
||||||
processMemoryManagementOption(switch, arg, pass, info, conf)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
of "mm":
|
if pass in {passCmd2, passPP}:
|
||||||
processMemoryManagementOption(switch, arg, pass, info, conf)
|
case arg.normalize
|
||||||
|
of "boehm":
|
||||||
|
conf.selectedGC = gcBoehm
|
||||||
|
defineSymbol(conf.symbols, "boehmgc")
|
||||||
|
incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
|
||||||
|
of "refc":
|
||||||
|
conf.selectedGC = gcRefc
|
||||||
|
of "markandsweep":
|
||||||
|
conf.selectedGC = gcMarkAndSweep
|
||||||
|
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||||
|
of "destructors", "arc":
|
||||||
|
conf.selectedGC = gcArc
|
||||||
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
|
defineSymbol(conf.symbols, "gcarc")
|
||||||
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
incl conf.globalOptions, optTinyRtti
|
||||||
|
if pass in {passCmd2, passPP}:
|
||||||
|
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||||
|
defineSymbol(conf.symbols, "nimV2")
|
||||||
|
if conf.exc == excNone and conf.backend != backendCpp:
|
||||||
|
conf.exc = excGoto
|
||||||
|
of "orc":
|
||||||
|
conf.selectedGC = gcOrc
|
||||||
|
defineSymbol(conf.symbols, "gcdestructors")
|
||||||
|
defineSymbol(conf.symbols, "gcorc")
|
||||||
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
incl conf.globalOptions, optTinyRtti
|
||||||
|
if pass in {passCmd2, passPP}:
|
||||||
|
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||||
|
defineSymbol(conf.symbols, "nimV2")
|
||||||
|
if conf.exc == excNone and conf.backend != backendCpp:
|
||||||
|
conf.exc = excGoto
|
||||||
|
of "hooks":
|
||||||
|
conf.selectedGC = gcHooks
|
||||||
|
defineSymbol(conf.symbols, "gchooks")
|
||||||
|
incl conf.globalOptions, optSeqDestructors
|
||||||
|
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
||||||
|
if pass in {passCmd2, passPP}:
|
||||||
|
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||||
|
of "go":
|
||||||
|
conf.selectedGC = gcGo
|
||||||
|
defineSymbol(conf.symbols, "gogc")
|
||||||
|
of "none":
|
||||||
|
conf.selectedGC = gcNone
|
||||||
|
defineSymbol(conf.symbols, "nogc")
|
||||||
|
of "stack", "regions":
|
||||||
|
conf.selectedGC = gcRegions
|
||||||
|
defineSymbol(conf.symbols, "gcregions")
|
||||||
|
of "v2": warningOptionNoop(arg)
|
||||||
|
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||||
of "warnings", "w":
|
of "warnings", "w":
|
||||||
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
||||||
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
||||||
|
|
@ -758,7 +700,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
|
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
|
||||||
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
|
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
|
||||||
of "threads":
|
of "threads":
|
||||||
if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
|
if conf.backend == backendJs: discard
|
||||||
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
||||||
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
|
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
|
||||||
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
|
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
|
||||||
|
|
@ -817,18 +759,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "clib":
|
of "clib":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if pass in {passCmd2, passPP}:
|
if pass in {passCmd2, passPP}:
|
||||||
conf.cLinkedLibs.add arg
|
conf.cLinkedLibs.add processPath(conf, arg, info).string
|
||||||
of "header":
|
of "header":
|
||||||
if conf != nil: conf.headerFile = arg
|
if conf != nil: conf.headerFile = arg
|
||||||
incl(conf.globalOptions, optGenIndex)
|
incl(conf.globalOptions, optGenIndex)
|
||||||
of "index":
|
of "index":
|
||||||
case arg.normalize
|
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
|
||||||
of "", "on": conf.globalOptions.incl {optGenIndex}
|
|
||||||
of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex}
|
|
||||||
of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly}
|
|
||||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
|
||||||
of "noimportdoc":
|
|
||||||
processOnOffSwitchG(conf, {optNoImportdoc}, arg, pass, info)
|
|
||||||
of "import":
|
of "import":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
if pass in {passCmd2, passPP}:
|
if pass in {passCmd2, passPP}:
|
||||||
|
|
@ -910,7 +846,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "v2": conf.symbolFiles = v2Sf
|
of "v2": conf.symbolFiles = v2Sf
|
||||||
of "stress": conf.symbolFiles = stressTest
|
of "stress": conf.symbolFiles = stressTest
|
||||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||||
setUseIc(conf.symbolFiles != disabledSf)
|
|
||||||
of "skipcfg":
|
of "skipcfg":
|
||||||
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
|
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
|
||||||
of "skipprojcfg":
|
of "skipprojcfg":
|
||||||
|
|
@ -923,8 +858,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
|
if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
|
||||||
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
||||||
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||||
of "gencdeps":
|
|
||||||
processOnOffSwitchG(conf, {optGenCDeps}, arg, pass, info)
|
|
||||||
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
||||||
of "lib":
|
of "lib":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
|
|
@ -934,9 +867,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
splitSwitch(conf, arg, key, val, pass, info)
|
splitSwitch(conf, arg, key, val, pass, info)
|
||||||
os.putEnv(key, val)
|
os.putEnv(key, val)
|
||||||
of "cc":
|
of "cc":
|
||||||
if conf.backend != backendJs: # bug #19330
|
expectArg(conf, switch, arg, pass, info)
|
||||||
expectArg(conf, switch, arg, pass, info)
|
setCC(conf, arg, info)
|
||||||
setCC(conf, arg, info)
|
|
||||||
of "track":
|
of "track":
|
||||||
expectArg(conf, switch, arg, pass, info)
|
expectArg(conf, switch, arg, pass, info)
|
||||||
track(conf, arg, info)
|
track(conf, arg, info)
|
||||||
|
|
@ -976,8 +908,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
excl(conf.notes, hintProcessing)
|
excl(conf.notes, hintProcessing)
|
||||||
excl(conf.mainPackageNotes, hintProcessing)
|
excl(conf.mainPackageNotes, hintProcessing)
|
||||||
else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg)
|
else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg)
|
||||||
of "unitsep":
|
|
||||||
conf.unitSep = if switchOn(arg): "\31" else: ""
|
|
||||||
of "listfullpaths":
|
of "listfullpaths":
|
||||||
# xxx in future work, use `warningDeprecated`
|
# xxx in future work, use `warningDeprecated`
|
||||||
conf.filenameOption = if switchOn(arg): foAbs else: foCanonical
|
conf.filenameOption = if switchOn(arg): foAbs else: foCanonical
|
||||||
|
|
@ -1008,9 +938,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
expectNoArg(conf, switch, arg, pass, info)
|
||||||
conf.exc = low(ExceptionSystem)
|
conf.exc = low(ExceptionSystem)
|
||||||
defineSymbol(conf.symbols, "noCppExceptions")
|
defineSymbol(conf.symbols, "noCppExceptions")
|
||||||
of "shownonexports":
|
|
||||||
expectNoArg(conf, switch, arg, pass, info)
|
|
||||||
showNonExportedFields(conf)
|
|
||||||
of "exceptions":
|
of "exceptions":
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "cpp": conf.exc = excCpp
|
of "cpp": conf.exc = excCpp
|
||||||
|
|
@ -1080,10 +1007,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||||
defineSymbol(conf.symbols, "NimMinor", "2")
|
defineSymbol(conf.symbols, "NimMinor", "2")
|
||||||
conf.globalOptions.incl optNimV12Emulation
|
conf.globalOptions.incl optNimV12Emulation
|
||||||
of "1.6":
|
|
||||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
|
||||||
defineSymbol(conf.symbols, "NimMinor", "6")
|
|
||||||
conf.globalOptions.incl optNimV16Emulation
|
|
||||||
else:
|
else:
|
||||||
localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
|
localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
|
||||||
# always be compatible with 1.x.100:
|
# always be compatible with 1.x.100:
|
||||||
|
|
@ -1108,8 +1031,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
|
processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
|
||||||
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
||||||
handleStdinInput(conf)
|
handleStdinInput(conf)
|
||||||
of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
|
of "nilseqs", "nilchecks", "mainmodule", "m", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
|
||||||
of "nimmainprefix": conf.nimMainPrefix = arg
|
|
||||||
else:
|
else:
|
||||||
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
|
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
|
||||||
else: invalidCmdLineOption(conf, pass, switch, info)
|
else: invalidCmdLineOption(conf, pass, switch, info)
|
||||||
|
|
@ -1146,8 +1068,6 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
|
||||||
else:
|
else:
|
||||||
if pass == passCmd1: config.commandArgs.add p.key
|
if pass == passCmd1: config.commandArgs.add p.key
|
||||||
if argsCount == 1:
|
if argsCount == 1:
|
||||||
if p.key.endsWith(".nims"):
|
|
||||||
incl(config.globalOptions, optWasNimscript)
|
|
||||||
# support UNIX style filenames everywhere for portable build scripts:
|
# support UNIX style filenames everywhere for portable build scripts:
|
||||||
if config.projectName.len == 0:
|
if config.projectName.len == 0:
|
||||||
config.projectName = unixToNativePath(p.key)
|
config.projectName = unixToNativePath(p.key)
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,11 @@
|
||||||
## for details. Note this is a first implementation and only the "Concept matching"
|
## for details. Note this is a first implementation and only the "Concept matching"
|
||||||
## section has been implemented.
|
## section has been implemented.
|
||||||
|
|
||||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets
|
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer,
|
||||||
|
types, intsets
|
||||||
|
|
||||||
from magicsys import addSonSkipIntLit
|
from magicsys import addSonSkipIntLit
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
const
|
const
|
||||||
logBindings = false
|
logBindings = false
|
||||||
|
|
||||||
|
|
@ -25,7 +23,7 @@ const
|
||||||
## --------------------------------------
|
## --------------------------------------
|
||||||
|
|
||||||
proc declareSelf(c: PContext; info: TLineInfo) =
|
proc declareSelf(c: PContext; info: TLineInfo) =
|
||||||
## Adds the magical 'Self' symbols to the current scope.
|
## adds the magical 'Self' symbols to the current scope.
|
||||||
let ow = getCurrOwner(c)
|
let ow = getCurrOwner(c)
|
||||||
let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info)
|
let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info)
|
||||||
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
|
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
|
||||||
|
|
@ -34,7 +32,7 @@ proc declareSelf(c: PContext; info: TLineInfo) =
|
||||||
addDecl(c, s, info)
|
addDecl(c, s, info)
|
||||||
|
|
||||||
proc isSelf*(t: PType): bool {.inline.} =
|
proc isSelf*(t: PType): bool {.inline.} =
|
||||||
## Is this the magical 'Self' type?
|
## is this the magical 'Self' type?
|
||||||
t.kind == tyTypeDesc and tfPacked in t.flags
|
t.kind == tyTypeDesc and tfPacked in t.flags
|
||||||
|
|
||||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
|
|
@ -47,8 +45,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
|
|
||||||
proc semConceptDecl(c: PContext; n: PNode): PNode =
|
proc semConceptDecl(c: PContext; n: PNode): PNode =
|
||||||
## Recursive helper for semantic checking for the concept declaration.
|
## Recursive helper for semantic checking for the concept declaration.
|
||||||
## Currently we only support (possibly empty) lists of statements
|
## Currently we only support lists of statements containing 'proc'
|
||||||
## containing 'proc' declarations and the like.
|
## declarations and the like.
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkStmtList, nkStmtListExpr:
|
of nkStmtList, nkStmtListExpr:
|
||||||
result = shallowCopy(n)
|
result = shallowCopy(n)
|
||||||
|
|
@ -61,10 +59,8 @@ proc semConceptDecl(c: PContext; n: PNode): PNode =
|
||||||
for i in 0..<n.len-1:
|
for i in 0..<n.len-1:
|
||||||
result[i] = n[i]
|
result[i] = n[i]
|
||||||
result[^1] = semConceptDecl(c, n[^1])
|
result[^1] = semConceptDecl(c, n[^1])
|
||||||
of nkCommentStmt:
|
|
||||||
discard
|
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
|
localError(c.config, n.info, "unexpected construct in the new-styled concept " & renderTree(n))
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
||||||
|
|
@ -101,7 +97,7 @@ proc existingBinding(m: MatchCon; key: PType): PType =
|
||||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||||
|
|
||||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
## 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
|
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||||
## of a routine that might match.
|
## of a routine that might match.
|
||||||
const
|
const
|
||||||
|
|
@ -274,7 +270,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
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]',
|
## 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.
|
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||||
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
|
let candidates = searchInScopesFilterBy(c, n[namePos].sym.name, kinds)
|
||||||
for candidate in candidates:
|
for candidate in candidates:
|
||||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||||
m.magic = candidate.magic
|
m.magic = candidate.magic
|
||||||
|
|
@ -316,7 +312,7 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invo
|
||||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
## 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'.)
|
## (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
|
## '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
|
## '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
|
## 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!
|
## we do not add any bindings at all on an unsuccessful match!
|
||||||
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ proc undefSymbol*(symbols: StringTableRef; symbol: string) =
|
||||||
# result = if isDefined(symbol): gSymbols[symbol] else: nil
|
# result = if isDefined(symbol): gSymbols[symbol] else: nil
|
||||||
|
|
||||||
iterator definedSymbolNames*(symbols: StringTableRef): string =
|
iterator definedSymbolNames*(symbols: StringTableRef): string =
|
||||||
for key in keys(symbols):
|
for key, val in pairs(symbols):
|
||||||
yield key
|
yield key
|
||||||
|
|
||||||
proc countDefinedSymbols*(symbols: StringTableRef): int =
|
proc countDefinedSymbols*(symbols: StringTableRef): int =
|
||||||
|
|
@ -70,7 +70,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimVmExportFixed") # deadcode
|
defineSymbol("nimVmExportFixed") # deadcode
|
||||||
defineSymbol("nimHasSymOwnerInMacro") # deadcode
|
defineSymbol("nimHasSymOwnerInMacro") # deadcode
|
||||||
defineSymbol("nimNewRuntime") # deadcode
|
defineSymbol("nimNewRuntime") # deadcode
|
||||||
defineSymbol("nimIncrSeqV3") # deadcode
|
defineSymbol("nimIncrSeqV3") # xxx: turn this into deadcode
|
||||||
defineSymbol("nimAshr") # deadcode
|
defineSymbol("nimAshr") # deadcode
|
||||||
defineSymbol("nimNoNilSeqs") # deadcode
|
defineSymbol("nimNoNilSeqs") # deadcode
|
||||||
defineSymbol("nimNoNilSeqs2") # deadcode
|
defineSymbol("nimNoNilSeqs2") # deadcode
|
||||||
|
|
@ -84,24 +84,10 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimHasSignatureHashInMacro") # deadcode
|
defineSymbol("nimHasSignatureHashInMacro") # deadcode
|
||||||
defineSymbol("nimHasDefault") # deadcode
|
defineSymbol("nimHasDefault") # deadcode
|
||||||
defineSymbol("nimMacrosSizealignof") # deadcode
|
defineSymbol("nimMacrosSizealignof") # deadcode
|
||||||
defineSymbol("nimNoZeroExtendMagic") # deadcode
|
|
||||||
defineSymbol("nimMacrosGetNodeId") # deadcode
|
|
||||||
defineSymbol("nimFixedForwardGeneric") # deadcode
|
|
||||||
defineSymbol("nimToOpenArrayCString") # deadcode
|
|
||||||
defineSymbol("nimHasUsed") # deadcode
|
|
||||||
defineSymbol("nimnomagic64") # deadcode
|
|
||||||
defineSymbol("nimNewShiftOps") # deadcode
|
|
||||||
defineSymbol("nimHasCursor") # deadcode
|
|
||||||
defineSymbol("nimAlignPragma") # deadcode
|
|
||||||
defineSymbol("nimHasExceptionsQuery") # deadcode
|
|
||||||
defineSymbol("nimHasIsNamedTuple") # deadcode
|
|
||||||
defineSymbol("nimHashOrdinalFixed") # deadcode
|
|
||||||
defineSymbol("nimHasSinkInference") # deadcode
|
|
||||||
defineSymbol("nimNewIntegerOps") # deadcode
|
|
||||||
defineSymbol("nimHasInvariant") # deadcode
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# > 0.20.0
|
||||||
|
defineSymbol("nimNoZeroExtendMagic")
|
||||||
|
defineSymbol("nimMacrosGetNodeId")
|
||||||
for f in Feature:
|
for f in Feature:
|
||||||
defineSymbol("nimHas" & $f)
|
defineSymbol("nimHas" & $f)
|
||||||
|
|
||||||
|
|
@ -112,18 +98,31 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
|
|
||||||
defineSymbol("nimFixedOwned")
|
defineSymbol("nimFixedOwned")
|
||||||
defineSymbol("nimHasStyleChecks")
|
defineSymbol("nimHasStyleChecks")
|
||||||
|
defineSymbol("nimToOpenArrayCString")
|
||||||
|
defineSymbol("nimHasUsed")
|
||||||
|
defineSymbol("nimFixedForwardGeneric")
|
||||||
|
defineSymbol("nimnomagic64")
|
||||||
|
defineSymbol("nimNewShiftOps")
|
||||||
|
defineSymbol("nimHasCursor")
|
||||||
|
defineSymbol("nimAlignPragma")
|
||||||
|
defineSymbol("nimHasExceptionsQuery")
|
||||||
|
defineSymbol("nimHasIsNamedTuple")
|
||||||
|
defineSymbol("nimHashOrdinalFixed")
|
||||||
|
|
||||||
when defined(nimHasLibFFI):
|
when defined(nimHasLibFFI):
|
||||||
# Renaming as we can't conflate input vs output define flags; e.g. this
|
# Renaming as we can't conflate input vs output define flags; e.g. this
|
||||||
# will report the right thing regardless of whether user adds
|
# will report the right thing regardless of whether user adds
|
||||||
# `-d:nimHasLibFFI` in his user config.
|
# `-d:nimHasLibFFI` in his user config.
|
||||||
defineSymbol("nimHasLibFFIEnabled") # deadcode
|
defineSymbol("nimHasLibFFIEnabled")
|
||||||
|
|
||||||
defineSymbol("nimHasStacktraceMsgs") # deadcode
|
defineSymbol("nimHasSinkInference")
|
||||||
|
defineSymbol("nimNewIntegerOps")
|
||||||
|
defineSymbol("nimHasInvariant")
|
||||||
|
defineSymbol("nimHasStacktraceMsgs")
|
||||||
defineSymbol("nimDoesntTrackDefects")
|
defineSymbol("nimDoesntTrackDefects")
|
||||||
defineSymbol("nimHasLentIterators") # deadcode
|
defineSymbol("nimHasLentIterators")
|
||||||
defineSymbol("nimHasDeclaredMagic") # deadcode
|
defineSymbol("nimHasDeclaredMagic")
|
||||||
defineSymbol("nimHasStacktracesModule") # deadcode
|
defineSymbol("nimHasStacktracesModule")
|
||||||
defineSymbol("nimHasEffectTraitsModule")
|
defineSymbol("nimHasEffectTraitsModule")
|
||||||
defineSymbol("nimHasCastPragmaBlocks")
|
defineSymbol("nimHasCastPragmaBlocks")
|
||||||
defineSymbol("nimHasDeclaredLocs")
|
defineSymbol("nimHasDeclaredLocs")
|
||||||
|
|
@ -134,23 +133,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimHasCustomLiterals")
|
defineSymbol("nimHasCustomLiterals")
|
||||||
defineSymbol("nimHasUnifiedTuple")
|
defineSymbol("nimHasUnifiedTuple")
|
||||||
defineSymbol("nimHasIterable")
|
defineSymbol("nimHasIterable")
|
||||||
defineSymbol("nimHasTypeofVoid") # deadcode
|
defineSymbol("nimHasTypeofVoid")
|
||||||
defineSymbol("nimHasDragonBox") # deadcode
|
|
||||||
defineSymbol("nimHasHintAll")
|
|
||||||
defineSymbol("nimHasTrace")
|
|
||||||
defineSymbol("nimHasEffectsOf")
|
|
||||||
|
|
||||||
defineSymbol("nimHasEnforceNoRaises")
|
|
||||||
defineSymbol("nimHasTopDownInference")
|
|
||||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
|
||||||
defineSymbol("nimHasCstringCase")
|
|
||||||
defineSymbol("nimHasCallsitePragma")
|
|
||||||
defineSymbol("nimHasAmbiguousEnumHint")
|
|
||||||
|
|
||||||
defineSymbol("nimHasWarnCastSizes")
|
|
||||||
defineSymbol("nimHasOutParams")
|
|
||||||
defineSymbol("nimHasSystemRaisesDefect")
|
|
||||||
defineSymbol("nimHasWarnUnnamedBreak")
|
|
||||||
defineSymbol("nimHasGenericDefine")
|
|
||||||
defineSymbol("nimHasDefineAliases")
|
|
||||||
defineSymbol("nimHasWarnBareExcept")
|
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,7 @@ Utilities to help with debugging nim compiler.
|
||||||
Experimental API, subject to change.
|
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 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
|
var conf0: ConfigRef
|
||||||
|
|
||||||
|
|
@ -36,37 +17,3 @@ proc onNewConfigRef*(conf: ConfigRef) {.inline.} =
|
||||||
proc getConfigRef*(): ConfigRef =
|
proc getConfigRef*(): ConfigRef =
|
||||||
## nil, if -d:nimDebugUtils wasn't specified
|
## nil, if -d:nimDebugUtils wasn't specified
|
||||||
result = conf0
|
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,10 @@
|
||||||
|
|
||||||
# This module implements a dependency file generator.
|
# This module implements a dependency file generator.
|
||||||
|
|
||||||
import options, ast, ropes, pathutils, msgs, lineinfos
|
import
|
||||||
|
options, ast, ropes, idents, passes, modulepaths, pathutils
|
||||||
import modulegraphs
|
|
||||||
|
|
||||||
import std/[os, parseutils]
|
|
||||||
import strutils except addf
|
|
||||||
import std/private/globs
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
|
from modulegraphs import ModuleGraph, PPassContext
|
||||||
|
|
||||||
type
|
type
|
||||||
TGen = object of PPassContext
|
TGen = object of PPassContext
|
||||||
|
|
@ -32,63 +25,21 @@ type
|
||||||
dotGraph: Rope
|
dotGraph: Rope
|
||||||
|
|
||||||
proc addDependencyAux(b: Backend; importing, imported: string) =
|
proc addDependencyAux(b: Backend; importing, imported: string) =
|
||||||
b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)])
|
b.dotGraph.addf("$1 -> \"$2\";$n", [rope(importing), rope(imported)])
|
||||||
# s1 -> s2_4[label="[0-9]"];
|
# s1 -> s2_4[label="[0-9]"];
|
||||||
|
|
||||||
proc toNimblePath(s: string, isStdlib: bool): string =
|
proc addDotDependency(c: PPassContext, n: PNode): PNode =
|
||||||
const stdPrefix = "std/"
|
|
||||||
const pkgPrefix = "pkg/"
|
|
||||||
if isStdlib:
|
|
||||||
let sub = "lib/"
|
|
||||||
var start = s.find(sub)
|
|
||||||
if start < 0:
|
|
||||||
doAssert false
|
|
||||||
else:
|
|
||||||
start += sub.len
|
|
||||||
let base = s[start..^1]
|
|
||||||
|
|
||||||
if base.startsWith("system") or base.startsWith("std"):
|
|
||||||
result = base
|
|
||||||
else:
|
|
||||||
for dir in stdlibDirs:
|
|
||||||
if base.startsWith(dir):
|
|
||||||
return stdPrefix & base.splitFile.name
|
|
||||||
|
|
||||||
result = stdPrefix & base
|
|
||||||
else:
|
|
||||||
var sub = getEnv("NIMBLE_DIR")
|
|
||||||
if sub.len == 0:
|
|
||||||
sub = ".nimble/pkgs/"
|
|
||||||
else:
|
|
||||||
sub.add "/pkgs/"
|
|
||||||
var start = s.find(sub)
|
|
||||||
if start < 0:
|
|
||||||
result = s
|
|
||||||
else:
|
|
||||||
start += sub.len
|
|
||||||
start += skipUntil(s, '/', start)
|
|
||||||
start += 1
|
|
||||||
result = pkgPrefix & s[start..^1]
|
|
||||||
|
|
||||||
proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
|
|
||||||
doAssert n.kind == nkSym, $n.kind
|
|
||||||
|
|
||||||
let path = splitFile(toProjPath(g.config, n.sym.position.FileIndex))
|
|
||||||
let modulePath = splitFile(toProjPath(g.config, g.module.position.FileIndex))
|
|
||||||
let parent = nativeToUnixPath(modulePath.dir / modulePath.name).toNimblePath(belongsToStdlib(g.graph, g.module))
|
|
||||||
let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
|
|
||||||
addDependencyAux(b, parent, child)
|
|
||||||
|
|
||||||
proc addDotDependency*(c: PPassContext, n: PNode): PNode =
|
|
||||||
result = n
|
result = n
|
||||||
let g = PGen(c)
|
let g = PGen(c)
|
||||||
let b = Backend(g.graph.backend)
|
let b = Backend(g.graph.backend)
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkImportStmt:
|
of nkImportStmt:
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
addDependency(c, g, b, n[i])
|
var imported = getModuleName(g.config, n[i])
|
||||||
|
addDependencyAux(b, g.module.name.s, imported)
|
||||||
of nkFromStmt, nkImportExceptStmt:
|
of nkFromStmt, nkImportExceptStmt:
|
||||||
addDependency(c, g, b, n[0])
|
var imported = getModuleName(g.config, n[0])
|
||||||
|
addDependencyAux(b, g.module.name.s, imported)
|
||||||
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
|
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
|
||||||
for i in 0..<n.len: discard addDotDependency(c, n[i])
|
for i in 0..<n.len: discard addDotDependency(c, n[i])
|
||||||
else:
|
else:
|
||||||
|
|
@ -100,12 +51,18 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
|
||||||
rope(project.splitFile.name), b.dotGraph],
|
rope(project.splitFile.name), b.dotGraph],
|
||||||
changeFileExt(project, "dot"))
|
changeFileExt(project, "dot"))
|
||||||
|
|
||||||
proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
when not defined(nimHasSinkInference):
|
||||||
|
{.pragma: nosinks.}
|
||||||
|
|
||||||
|
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
|
||||||
var g: PGen
|
var g: PGen
|
||||||
new(g)
|
new(g)
|
||||||
g.module = module
|
g.module = module
|
||||||
g.config = graph.config
|
g.config = graph.config
|
||||||
g.graph = graph
|
g.graph = graph
|
||||||
if graph.backend == nil:
|
if graph.backend == nil:
|
||||||
graph.backend = Backend(dotGraph: "")
|
graph.backend = Backend(dotGraph: nil)
|
||||||
result = g
|
result = g
|
||||||
|
|
||||||
|
const gendependPass* = makePass(open = myOpen, process = addDotDependency)
|
||||||
|
|
||||||
|
|
|
||||||
551
compiler/dfa.nim
551
compiler/dfa.nim
|
|
@ -9,33 +9,37 @@
|
||||||
|
|
||||||
## Data flow analysis for Nim.
|
## Data flow analysis for Nim.
|
||||||
## We transform the AST into a linear list of instructions first to
|
## 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'
|
## instructions: 'goto X' is an unconditional goto, 'fork X'
|
||||||
## is a conditional goto (either the next instruction or 'X' can be
|
## is a conditional goto (either the next instruction or 'X' can be
|
||||||
## taken), 'loop X' is the only jump that jumps back.
|
## taken). Exhaustive case statements are translated
|
||||||
##
|
|
||||||
## Exhaustive case statements are translated
|
|
||||||
## so that the last branch is transformed into an 'else' branch.
|
## so that the last branch is transformed into an 'else' branch.
|
||||||
## ``return`` and ``break`` are all covered by 'goto'.
|
## ``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
|
## The data structures and algorithms used here are inspired by
|
||||||
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
|
## "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
|
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
|
||||||
|
|
||||||
import ast, intsets, lineinfos, renderer, aliasanalysis
|
import ast, types, intsets, lineinfos, renderer
|
||||||
import std/private/asciitables
|
import std/private/asciitables
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
InstrKind* = enum
|
InstrKind* = enum
|
||||||
goto, loop, fork, def, use
|
goto, fork, def, use
|
||||||
Instr* = object
|
Instr* = object
|
||||||
|
n*: PNode # contains the def/use location.
|
||||||
case kind*: InstrKind
|
case kind*: InstrKind
|
||||||
of goto, fork, loop: dest*: int
|
of goto, fork: dest*: int
|
||||||
of def, use:
|
else: discard
|
||||||
n*: PNode # contains the def/use location.
|
|
||||||
|
|
||||||
ControlFlowGraph* = seq[Instr]
|
ControlFlowGraph* = seq[Instr]
|
||||||
|
|
||||||
|
|
@ -52,10 +56,9 @@ type
|
||||||
|
|
||||||
Con = object
|
Con = object
|
||||||
code: ControlFlowGraph
|
code: ControlFlowGraph
|
||||||
inTryStmt, interestingInstructions: int
|
inTryStmt: int
|
||||||
blocks: seq[TBlock]
|
blocks: seq[TBlock]
|
||||||
owner: PSym
|
owner: PSym
|
||||||
root: PSym
|
|
||||||
|
|
||||||
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||||
# for debugging purposes
|
# for debugging purposes
|
||||||
|
|
@ -63,7 +66,7 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||||
var jumpTargets = initIntSet()
|
var jumpTargets = initIntSet()
|
||||||
let last = if last < 0: c.len-1 else: min(last, c.len-1)
|
let last = if last < 0: c.len-1 else: min(last, c.len-1)
|
||||||
for i in start..last:
|
for i in start..last:
|
||||||
if c[i].kind in {goto, fork, loop}:
|
if c[i].kind in {goto, fork}:
|
||||||
jumpTargets.incl(i+c[i].dest)
|
jumpTargets.incl(i+c[i].dest)
|
||||||
var i = start
|
var i = start
|
||||||
while i <= last:
|
while i <= last:
|
||||||
|
|
@ -74,12 +77,12 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||||
case c[i].kind
|
case c[i].kind
|
||||||
of def, use:
|
of def, use:
|
||||||
result.add renderTree(c[i].n)
|
result.add renderTree(c[i].n)
|
||||||
result.add("\t#")
|
of goto, fork:
|
||||||
result.add($c[i].n.info.line)
|
|
||||||
result.add("\n")
|
|
||||||
of goto, fork, loop:
|
|
||||||
result.add "L"
|
result.add "L"
|
||||||
result.addInt c[i].dest+i
|
result.addInt c[i].dest+i
|
||||||
|
result.add("\t#")
|
||||||
|
result.add($c[i].n.info.line)
|
||||||
|
result.add("\n")
|
||||||
inc i
|
inc i
|
||||||
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
||||||
|
|
||||||
|
|
@ -87,13 +90,181 @@ proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
|
||||||
## echos the ControlFlowGraph for debugging purposes.
|
## echos the ControlFlowGraph for debugging purposes.
|
||||||
echo codeListing(c, start, last).alignTable
|
echo codeListing(c, start, last).alignTable
|
||||||
|
|
||||||
proc forkI(c: var Con): TPosition =
|
proc forkI(c: var Con; n: PNode): TPosition =
|
||||||
result = TPosition(c.code.len)
|
result = TPosition(c.code.len)
|
||||||
c.code.add Instr(kind: fork, dest: 0)
|
c.code.add Instr(n: n, kind: fork, dest: 0)
|
||||||
|
|
||||||
proc gotoI(c: var Con): TPosition =
|
proc gotoI(c: var Con; n: PNode): TPosition =
|
||||||
result = TPosition(c.code.len)
|
result = TPosition(c.code.len)
|
||||||
c.code.add Instr(kind: goto, dest: 0)
|
c.code.add Instr(n: n, kind: goto, dest: 0)
|
||||||
|
|
||||||
|
#[
|
||||||
|
|
||||||
|
Join is no more
|
||||||
|
===============
|
||||||
|
Instead of generating join instructions we adapt our traversal of the CFG.
|
||||||
|
|
||||||
|
When encountering a fork we split into two paths, we follow the path
|
||||||
|
starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
|
||||||
|
If we encounter gotos that would jump further than the current joinpoint,
|
||||||
|
as can happen with gotos generated by unstructured controlflow such as break, raise or return,
|
||||||
|
we simply suspend following the current path, and follow the other path until the new joinpoint
|
||||||
|
which is simply the instruction pointer returned to us by the now suspended path.
|
||||||
|
If the path we are following now, also encounters a goto that exceeds the joinpoint
|
||||||
|
we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
|
||||||
|
If we eventually reach a common joinpoint we join the two paths.
|
||||||
|
This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
|
||||||
|
cutting down on the CFG size but is also mandatory for correctly handling complicated cases
|
||||||
|
of unstructured controlflow.
|
||||||
|
|
||||||
|
|
||||||
|
Design of join
|
||||||
|
==============
|
||||||
|
|
||||||
|
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 genLabel(c: Con): TPosition = TPosition(c.code.len)
|
proc genLabel(c: Con): TPosition = TPosition(c.code.len)
|
||||||
|
|
||||||
|
|
@ -101,8 +272,8 @@ template checkedDistance(dist): int =
|
||||||
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
|
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
|
||||||
dist
|
dist
|
||||||
|
|
||||||
proc jmpBack(c: var Con, p = TPosition(0)) =
|
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
|
||||||
c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))
|
c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
|
||||||
|
|
||||||
proc patch(c: var Con, p: TPosition) =
|
proc patch(c: var Con, p: TPosition) =
|
||||||
# patch with current index
|
# patch with current index
|
||||||
|
|
@ -112,12 +283,12 @@ proc gen(c: var Con; n: PNode)
|
||||||
|
|
||||||
proc popBlock(c: var Con; oldLen: int) =
|
proc popBlock(c: var Con; oldLen: int) =
|
||||||
var exits: seq[TPosition]
|
var exits: seq[TPosition]
|
||||||
exits.add c.gotoI()
|
exits.add c.gotoI(newNode(nkEmpty))
|
||||||
for f in c.blocks[oldLen].breakFixups:
|
for f in c.blocks[oldLen].breakFixups:
|
||||||
c.patch(f[0])
|
c.patch(f[0])
|
||||||
for finale in f[1]:
|
for finale in f[1]:
|
||||||
c.gen(finale)
|
c.gen(finale)
|
||||||
exits.add c.gotoI()
|
exits.add c.gotoI(newNode(nkEmpty))
|
||||||
for e in exits:
|
for e in exits:
|
||||||
c.patch e
|
c.patch e
|
||||||
c.blocks.setLen(oldLen)
|
c.blocks.setLen(oldLen)
|
||||||
|
|
@ -132,29 +303,86 @@ proc isTrue(n: PNode): bool =
|
||||||
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
|
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
|
||||||
n.kind == nkIntLit and n.intVal != 0
|
n.kind == nkIntLit and n.intVal != 0
|
||||||
|
|
||||||
template forkT(body) =
|
when true:
|
||||||
let lab1 = c.forkI()
|
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:
|
||||||
|
|
||||||
|
block:
|
||||||
|
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[0]):
|
||||||
|
# 'while true' is an idiom in Nim and so we produce
|
||||||
|
# better code for it:
|
||||||
|
withBlock(nil):
|
||||||
|
for i in 0..2:
|
||||||
|
c.gen(n[1])
|
||||||
|
else:
|
||||||
|
withBlock(nil):
|
||||||
|
var endings: array[3, TPosition]
|
||||||
|
for i in 0..2:
|
||||||
|
c.gen(n[0])
|
||||||
|
endings[i] = c.forkI(n)
|
||||||
|
c.gen(n[1])
|
||||||
|
for i in countdown(endings.high, 0):
|
||||||
|
c.patch(endings[i])
|
||||||
|
|
||||||
|
else:
|
||||||
|
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(n, lab1)
|
||||||
|
else:
|
||||||
|
c.gen(n[0])
|
||||||
|
forkT(n):
|
||||||
|
c.gen(n[1])
|
||||||
|
c.jmpBack(n, lab1)
|
||||||
|
|
||||||
|
template forkT(n, body) =
|
||||||
|
let lab1 = c.forkI(n)
|
||||||
body
|
body
|
||||||
c.patch(lab1)
|
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)
|
|
||||||
|
|
||||||
proc genIf(c: var Con, n: PNode) =
|
proc genIf(c: var Con, n: PNode) =
|
||||||
#[
|
#[
|
||||||
|
|
||||||
|
|
@ -193,22 +421,15 @@ proc genIf(c: var Con, n: PNode) =
|
||||||
|
|
||||||
]#
|
]#
|
||||||
var endings: seq[TPosition] = @[]
|
var endings: seq[TPosition] = @[]
|
||||||
let oldInteresting = c.interestingInstructions
|
|
||||||
let oldLen = c.code.len
|
|
||||||
|
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
let it = n[i]
|
let it = n[i]
|
||||||
c.gen(it[0])
|
c.gen(it[0])
|
||||||
if it.len == 2:
|
if it.len == 2:
|
||||||
forkT:
|
forkT(it[1]):
|
||||||
c.gen(it.lastSon)
|
c.gen(it[1])
|
||||||
endings.add c.gotoI()
|
endings.add c.gotoI(it[1])
|
||||||
|
for i in countdown(endings.high, 0):
|
||||||
if oldInteresting == c.interestingInstructions:
|
c.patch(endings[i])
|
||||||
setLen c.code, oldLen
|
|
||||||
else:
|
|
||||||
for i in countdown(endings.high, 0):
|
|
||||||
c.patch(endings[i])
|
|
||||||
|
|
||||||
proc genAndOr(c: var Con; n: PNode) =
|
proc genAndOr(c: var Con; n: PNode) =
|
||||||
# asgn dest, a
|
# asgn dest, a
|
||||||
|
|
@ -217,7 +438,7 @@ proc genAndOr(c: var Con; n: PNode) =
|
||||||
# lab1:
|
# lab1:
|
||||||
# join F1
|
# join F1
|
||||||
c.gen(n[1])
|
c.gen(n[1])
|
||||||
forkT:
|
forkT(n):
|
||||||
c.gen(n[2])
|
c.gen(n[2])
|
||||||
|
|
||||||
proc genCase(c: var Con; n: PNode) =
|
proc genCase(c: var Con; n: PNode) =
|
||||||
|
|
@ -232,34 +453,34 @@ proc genCase(c: var Con; n: PNode) =
|
||||||
# elsePart
|
# elsePart
|
||||||
# Lend:
|
# Lend:
|
||||||
let isExhaustive = skipTypes(n[0].typ,
|
let isExhaustive = skipTypes(n[0].typ,
|
||||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
|
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
||||||
|
|
||||||
|
# we generate endings as a set of chained gotos, this is a bit awkward but it
|
||||||
|
# ensures when recursively traversing the CFG for various analysis, we don't
|
||||||
|
# artificially extended the life of each branch (for the purposes of DFA)
|
||||||
|
# beyond the minimum amount.
|
||||||
var endings: seq[TPosition] = @[]
|
var endings: seq[TPosition] = @[]
|
||||||
c.gen(n[0])
|
c.gen(n[0])
|
||||||
let oldInteresting = c.interestingInstructions
|
|
||||||
let oldLen = c.code.len
|
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
let it = n[i]
|
let it = n[i]
|
||||||
if it.len == 1 or (i == n.len-1 and isExhaustive):
|
if it.len == 1 or (i == n.len-1 and isExhaustive):
|
||||||
# treat the last branch as 'else' if this is an exhaustive case statement.
|
# treat the last branch as 'else' if this is an exhaustive case statement.
|
||||||
c.gen(it.lastSon)
|
c.gen(it.lastSon)
|
||||||
|
if endings.len != 0:
|
||||||
|
c.patch(endings[^1])
|
||||||
else:
|
else:
|
||||||
forkT:
|
forkT(it.lastSon):
|
||||||
c.gen(it.lastSon)
|
c.gen(it.lastSon)
|
||||||
endings.add c.gotoI()
|
if endings.len != 0:
|
||||||
|
c.patch(endings[^1])
|
||||||
if oldInteresting == c.interestingInstructions:
|
endings.add c.gotoI(it.lastSon)
|
||||||
setLen c.code, oldLen
|
|
||||||
else:
|
|
||||||
for i in countdown(endings.high, 0):
|
|
||||||
c.patch(endings[i])
|
|
||||||
|
|
||||||
proc genBlock(c: var Con; n: PNode) =
|
proc genBlock(c: var Con; n: PNode) =
|
||||||
withBlock(n[0].sym):
|
withBlock(n[0].sym):
|
||||||
c.gen(n[1])
|
c.gen(n[1])
|
||||||
|
|
||||||
proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
||||||
let lab1 = c.gotoI()
|
let lab1 = c.gotoI(n)
|
||||||
if c.blocks[i].isTryBlock:
|
if c.blocks[i].isTryBlock:
|
||||||
c.blocks[i].raiseFixups.add lab1
|
c.blocks[i].raiseFixups.add lab1
|
||||||
else:
|
else:
|
||||||
|
|
@ -272,7 +493,6 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
||||||
c.blocks[i].breakFixups.add (lab1, trailingFinales)
|
c.blocks[i].breakFixups.add (lab1, trailingFinales)
|
||||||
|
|
||||||
proc genBreak(c: var Con; n: PNode) =
|
proc genBreak(c: var Con; n: PNode) =
|
||||||
inc c.interestingInstructions
|
|
||||||
if n[0].kind == nkSym:
|
if n[0].kind == nkSym:
|
||||||
for i in countdown(c.blocks.high, 0):
|
for i in countdown(c.blocks.high, 0):
|
||||||
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
|
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
|
||||||
|
|
@ -303,9 +523,9 @@ proc genTry(c: var Con; n: PNode) =
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
let it = n[i]
|
let it = n[i]
|
||||||
if it.kind != nkFinally:
|
if it.kind != nkFinally:
|
||||||
forkT:
|
forkT(it):
|
||||||
c.gen(it.lastSon)
|
c.gen(it.lastSon)
|
||||||
endings.add c.gotoI()
|
endings.add c.gotoI(it)
|
||||||
for i in countdown(endings.high, 0):
|
for i in countdown(endings.high, 0):
|
||||||
c.patch(endings[i])
|
c.patch(endings[i])
|
||||||
|
|
||||||
|
|
@ -313,12 +533,11 @@ proc genTry(c: var Con; n: PNode) =
|
||||||
if fin.kind == nkFinally:
|
if fin.kind == nkFinally:
|
||||||
c.gen(fin[0])
|
c.gen(fin[0])
|
||||||
|
|
||||||
template genNoReturn(c: var Con) =
|
template genNoReturn(c: var Con; n: PNode) =
|
||||||
# leave the graph
|
# 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) =
|
proc genRaise(c: var Con; n: PNode) =
|
||||||
inc c.interestingInstructions
|
|
||||||
gen(c, n[0])
|
gen(c, n[0])
|
||||||
if c.inTryStmt > 0:
|
if c.inTryStmt > 0:
|
||||||
for i in countdown(c.blocks.high, 0):
|
for i in countdown(c.blocks.high, 0):
|
||||||
|
|
@ -327,14 +546,13 @@ proc genRaise(c: var Con; n: PNode) =
|
||||||
return
|
return
|
||||||
assert false #Unreachable
|
assert false #Unreachable
|
||||||
else:
|
else:
|
||||||
genNoReturn(c)
|
genNoReturn(c, n)
|
||||||
|
|
||||||
proc genImplicitReturn(c: var Con) =
|
proc genImplicitReturn(c: var Con) =
|
||||||
if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
|
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[resultPos])
|
||||||
|
|
||||||
proc genReturn(c: var Con; n: PNode) =
|
proc genReturn(c: var Con; n: PNode) =
|
||||||
inc c.interestingInstructions
|
|
||||||
if n[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
gen(c, n[0])
|
gen(c, n[0])
|
||||||
else:
|
else:
|
||||||
|
|
@ -343,6 +561,124 @@ proc genReturn(c: var Con; n: PNode) =
|
||||||
|
|
||||||
const
|
const
|
||||||
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
||||||
|
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
|
||||||
|
|
||||||
|
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[0]: true
|
||||||
|
# x[0] -> x: false
|
||||||
|
# x[0] -> x[0]: true
|
||||||
|
# x[0] -> x[1]: false
|
||||||
|
# x -> x[i]: true
|
||||||
|
# x[i] -> x: false
|
||||||
|
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
|
||||||
|
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
|
||||||
|
template collectImportantNodes(result, n) =
|
||||||
|
var result: seq[PNode]
|
||||||
|
var n = n
|
||||||
|
while true:
|
||||||
|
case n.kind
|
||||||
|
of PathKinds0 - {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}:
|
||||||
|
n = n[0]
|
||||||
|
of PathKinds1:
|
||||||
|
n = n[1]
|
||||||
|
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||||
|
result.add n
|
||||||
|
n = n[0]
|
||||||
|
of nkSym:
|
||||||
|
result.add n; break
|
||||||
|
else: return no
|
||||||
|
|
||||||
|
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 nkCheckedFieldExpr:
|
||||||
|
if currFieldPath[0][1].sym != currObjPath[0][1].sym: return no
|
||||||
|
of nkBracketExpr:
|
||||||
|
if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
|
||||||
|
if currFieldPath[1].intVal != currObjPath[1].intVal:
|
||||||
|
return no
|
||||||
|
else:
|
||||||
|
result = maybe
|
||||||
|
else: assert false # unreachable
|
||||||
|
|
||||||
|
proc 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.
|
||||||
|
|
||||||
proc skipTrivials(c: var Con, n: PNode): PNode =
|
proc skipTrivials(c: var Con, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -361,9 +697,8 @@ proc genUse(c: var Con; orig: PNode) =
|
||||||
let n = c.skipTrivials(orig)
|
let n = c.skipTrivials(orig)
|
||||||
|
|
||||||
if n.kind == nkSym:
|
if n.kind == nkSym:
|
||||||
if n.sym.kind in InterestingSyms and n.sym == c.root:
|
if n.sym.kind in InterestingSyms:
|
||||||
c.code.add Instr(kind: use, n: orig)
|
c.code.add Instr(n: orig, kind: use)
|
||||||
inc c.interestingInstructions
|
|
||||||
else:
|
else:
|
||||||
gen(c, n)
|
gen(c, n)
|
||||||
|
|
||||||
|
|
@ -371,9 +706,7 @@ proc genDef(c: var Con; orig: PNode) =
|
||||||
let n = c.skipTrivials(orig)
|
let n = c.skipTrivials(orig)
|
||||||
|
|
||||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
||||||
if n.sym == c.root:
|
c.code.add Instr(n: orig, kind: def)
|
||||||
c.code.add Instr(kind: def, n: orig)
|
|
||||||
inc c.interestingInstructions
|
|
||||||
|
|
||||||
proc genCall(c: var Con; n: PNode) =
|
proc genCall(c: var Con; n: PNode) =
|
||||||
gen(c, n[0])
|
gen(c, n[0])
|
||||||
|
|
@ -381,17 +714,18 @@ proc genCall(c: var Con; n: PNode) =
|
||||||
if t != nil: t = t.skipTypes(abstractInst)
|
if t != nil: t = t.skipTypes(abstractInst)
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
gen(c, n[i])
|
gen(c, n[i])
|
||||||
if t != nil and i < t.len and isOutParam(t[i]):
|
when false:
|
||||||
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
|
if t != nil and i < t.len and t[i].kind == tyOut:
|
||||||
genDef(c, n[i])
|
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
|
||||||
|
genDef(c, n[i])
|
||||||
# every call can potentially raise:
|
# every call can potentially raise:
|
||||||
if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]):
|
if c.inTryStmt > 0 and canRaiseConservative(n[0]):
|
||||||
# we generate the instruction sequence:
|
# we generate the instruction sequence:
|
||||||
# fork lab1
|
# fork lab1
|
||||||
# goto exceptionHandler (except or finally)
|
# goto exceptionHandler (except or finally)
|
||||||
# lab1:
|
# lab1:
|
||||||
# join F1
|
# join F1
|
||||||
forkT:
|
forkT(n):
|
||||||
for i in countdown(c.blocks.high, 0):
|
for i in countdown(c.blocks.high, 0):
|
||||||
if c.blocks[i].isTryBlock:
|
if c.blocks[i].isTryBlock:
|
||||||
genBreakOrRaiseAux(c, i, n)
|
genBreakOrRaiseAux(c, i, n)
|
||||||
|
|
@ -429,18 +763,12 @@ proc gen(c: var Con; n: PNode) =
|
||||||
else:
|
else:
|
||||||
genCall(c, n)
|
genCall(c, n)
|
||||||
if sfNoReturn in n[0].sym.flags:
|
if sfNoReturn in n[0].sym.flags:
|
||||||
genNoReturn(c)
|
genNoReturn(c, n)
|
||||||
else:
|
else:
|
||||||
genCall(c, n)
|
genCall(c, n)
|
||||||
of nkCharLit..nkNilLit: discard
|
of nkCharLit..nkNilLit: discard
|
||||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
gen(c, n[1])
|
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
|
# watch out: 'obj[i].f2 = value' sets 'f2' but
|
||||||
# "uses" 'i'. But we are only talking about builtin array indexing so
|
# "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'.
|
# it doesn't matter and 'x = 34' is NOT a usage of 'x'.
|
||||||
|
|
@ -470,32 +798,13 @@ proc gen(c: var Con; n: PNode) =
|
||||||
of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
|
of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
when false:
|
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
|
||||||
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 =
|
|
||||||
## constructs a control flow graph for ``body``.
|
## constructs a control flow graph for ``body``.
|
||||||
var c = Con(code: @[], blocks: @[], owner: s, root: root)
|
var c = Con(code: @[], blocks: @[], owner: s)
|
||||||
withBlock(s):
|
withBlock(s):
|
||||||
gen(c, body)
|
gen(c, body)
|
||||||
if root.kind == skResult:
|
genImplicitReturn(c)
|
||||||
genImplicitReturn(c)
|
|
||||||
when defined(gcArc) or defined(gcOrc):
|
when defined(gcArc) or defined(gcOrc):
|
||||||
result = c.code # will move
|
result = c.code # will move
|
||||||
else:
|
else:
|
||||||
shallowCopy(result, c.code)
|
shallowCopy(result, c.code)
|
||||||
when false:
|
|
||||||
optimizeJumps result
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,7 @@
|
||||||
# semantic checking.
|
# semantic checking.
|
||||||
|
|
||||||
import
|
import
|
||||||
options, ast, msgs, docgen, lineinfos, pathutils, packages
|
options, ast, msgs, passes, docgen, lineinfos, pathutils
|
||||||
|
|
||||||
from modulegraphs import ModuleGraph, PPassContext
|
from modulegraphs import ModuleGraph, PPassContext
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ type
|
||||||
PGen = ref TGen
|
PGen = ref TGen
|
||||||
|
|
||||||
proc shouldProcess(g: PGen): bool =
|
proc shouldProcess(g: PGen): bool =
|
||||||
(optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or
|
(optWholeProject in g.doc.conf.globalOptions and g.module.getnimblePkgId == g.doc.conf.mainPackageId) or
|
||||||
sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
|
sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
|
||||||
|
|
||||||
template closeImpl(body: untyped) {.dirty.} =
|
template closeImpl(body: untyped) {.dirty.} =
|
||||||
|
|
@ -31,28 +31,27 @@ template closeImpl(body: untyped) {.dirty.} =
|
||||||
let useWarning = sfMainModule notin g.module.flags
|
let useWarning = sfMainModule notin g.module.flags
|
||||||
let groupedToc = true
|
let groupedToc = true
|
||||||
if shouldProcess(g):
|
if shouldProcess(g):
|
||||||
finishGenerateDoc(g.doc)
|
|
||||||
body
|
body
|
||||||
try:
|
try:
|
||||||
generateIndex(g.doc)
|
generateIndex(g.doc)
|
||||||
except IOError:
|
except IOError:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||||
closeImpl:
|
closeImpl:
|
||||||
writeOutput(g.doc, useWarning, groupedToc)
|
writeOutput(g.doc, useWarning, groupedToc)
|
||||||
|
|
||||||
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||||
closeImpl:
|
closeImpl:
|
||||||
writeOutputJson(g.doc, useWarning)
|
writeOutputJson(g.doc, useWarning)
|
||||||
|
|
||||||
proc processNode*(c: PPassContext, n: PNode): PNode =
|
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
var g = PGen(c)
|
var g = PGen(c)
|
||||||
if shouldProcess(g):
|
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
|
result = n
|
||||||
var g = PGen(c)
|
var g = PGen(c)
|
||||||
if shouldProcess(g):
|
if shouldProcess(g):
|
||||||
|
|
@ -64,15 +63,25 @@ template myOpenImpl(ext: untyped) {.dirty.} =
|
||||||
g.module = module
|
g.module = module
|
||||||
g.config = graph.config
|
g.config = graph.config
|
||||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
||||||
graph.cache, graph.config, ext, module, hasToc = true)
|
graph.cache, graph.config, ext, module)
|
||||||
|
d.hasToc = true
|
||||||
g.doc = d
|
g.doc = d
|
||||||
result = g
|
result = g
|
||||||
|
|
||||||
proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||||
myOpenImpl(HtmlExt)
|
myOpenImpl(HtmlExt)
|
||||||
|
|
||||||
proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||||
myOpenImpl(TexExt)
|
myOpenImpl(TexExt)
|
||||||
|
|
||||||
proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||||
myOpenImpl(JsonExt)
|
myOpenImpl(JsonExt)
|
||||||
|
|
||||||
|
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
||||||
|
const docgen2TexPass* = makePass(open = myOpenTex, process = processNode,
|
||||||
|
close = close)
|
||||||
|
const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
|
||||||
|
close = closeJson)
|
||||||
|
|
||||||
|
proc finishDoc2Pass*(project: string) =
|
||||||
|
discard
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
|
|
||||||
import ast, idents, lineinfos, modulegraphs, magicsys
|
import ast, idents, lineinfos, modulegraphs, magicsys
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
|
|
||||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||||
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
||||||
|
|
||||||
|
|
@ -30,7 +26,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||||
assert(t.n[i].kind == nkSym)
|
assert(t.n[i].kind == nkSym)
|
||||||
var field = t.n[i].sym
|
var field = t.n[i].sym
|
||||||
let val = if field.ast == nil: field.name.s else: field.ast.strVal
|
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))))
|
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
||||||
#newIntTypeNode(nkIntLit, field.position, t)
|
#newIntTypeNode(nkIntLit, field.position, t)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
|
|
||||||
import ast, renderer, options, strutils, types
|
import ast, renderer, options, strutils, types
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
ErrorKind* = enum ## expand as you need.
|
ErrorKind* = enum ## expand as you need.
|
||||||
RawTypeMismatchError
|
RawTypeMismatchError
|
||||||
|
|
|
||||||
|
|
@ -65,19 +65,19 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
|
||||||
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
|
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
|
||||||
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
|
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
|
||||||
# first try this exe itself:
|
# first try this exe itself:
|
||||||
theAddr = gExeHandle.symAddr(name.cstring)
|
theAddr = gExeHandle.symAddr(name)
|
||||||
# then try libc:
|
# then try libc:
|
||||||
if theAddr.isNil:
|
if theAddr.isNil:
|
||||||
let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
|
let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
|
||||||
theAddr = dllhandle.symAddr(name.cstring)
|
theAddr = dllhandle.symAddr(name)
|
||||||
elif not lib.isNil:
|
elif not lib.isNil:
|
||||||
let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
|
let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
|
||||||
libPathMsg = dll
|
libPathMsg = dll
|
||||||
let dllhandle = getDll(conf, gDllCache, dll, sym.info)
|
let dllhandle = getDll(conf, gDllCache, dll, sym.info)
|
||||||
theAddr = dllhandle.symAddr(name.cstring)
|
theAddr = dllhandle.symAddr(name)
|
||||||
if theAddr.isNil: globalError(conf, sym.info,
|
if theAddr.isNil: globalError(conf, sym.info,
|
||||||
"cannot import symbol: " & name & " from " & libPathMsg)
|
"cannot import symbol: " & name & " from " & libPathMsg)
|
||||||
result.intVal = cast[int](theAddr)
|
result.intVal = cast[ByteAddress](theAddr)
|
||||||
|
|
||||||
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
||||||
if t == nil: return addr libffi.type_void
|
if t == nil: return addr libffi.type_void
|
||||||
|
|
@ -110,10 +110,10 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI
|
||||||
else:
|
else:
|
||||||
globalError(conf, info, "cannot map calling convention to FFI")
|
globalError(conf, info, "cannot map calling convention to FFI")
|
||||||
|
|
||||||
template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
|
template rd(T, p: untyped): untyped = (cast[ptr T](p))[]
|
||||||
template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
|
template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
|
||||||
template `+!`(x, y: untyped): untyped =
|
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 =
|
proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
|
||||||
## computes the size of the blob
|
## computes the size of the blob
|
||||||
|
|
@ -177,8 +177,8 @@ const maxPackDepth = 20
|
||||||
var packRecCheck = 0
|
var packRecCheck = 0
|
||||||
|
|
||||||
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
|
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
|
||||||
template awr(typ, v: untyped): untyped =
|
template awr(T, v: untyped): untyped =
|
||||||
wr(typ, res, v)
|
wr(T, res, v)
|
||||||
|
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyBool: awr(bool, v.intVal != 0)
|
of tyBool: awr(bool, v.intVal != 0)
|
||||||
|
|
@ -369,13 +369,13 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||||
# in their unboxed representation so nothing it to be unpacked:
|
# in their unboxed representation so nothing it to be unpacked:
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
awi(nkPtrLit, cast[int](p))
|
awi(nkPtrLit, cast[ByteAddress](p))
|
||||||
of tyPtr, tyRef, tyVar, tyLent:
|
of tyPtr, tyRef, tyVar, tyLent:
|
||||||
let p = rd(pointer, x)
|
let p = rd(pointer, x)
|
||||||
if p.isNil:
|
if p.isNil:
|
||||||
setNil()
|
setNil()
|
||||||
elif n == nil or n.kind == nkPtrLit:
|
elif n == nil or n.kind == nkPtrLit:
|
||||||
awi(nkPtrLit, cast[int](p))
|
awi(nkPtrLit, cast[ByteAddress](p))
|
||||||
elif n != nil and n.len == 1:
|
elif n != nil and n.len == 1:
|
||||||
internalAssert(conf, n.kind == nkRefTy)
|
internalAssert(conf, n.kind == nkRefTy)
|
||||||
n[0] = unpack(conf, p, typ.lastSon, n[0])
|
n[0] = unpack(conf, p, typ.lastSon, n[0])
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type
|
||||||
|
|
||||||
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
||||||
result = copyNode(a)
|
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) =
|
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
template handleParam(param) =
|
template handleParam(param) =
|
||||||
|
|
@ -83,14 +83,10 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
not c.isDeclarative:
|
not c.isDeclarative:
|
||||||
c.isDeclarative = true
|
c.isDeclarative = true
|
||||||
isDeclarative = true
|
isDeclarative = true
|
||||||
if (not c.isDeclarative) and templ.kind in nkCallKinds and isRunnableExamples(templ[0]):
|
var res = copyNode(c, templ, actual)
|
||||||
# fixes bug #16993, bug #18054
|
for i in 0..<templ.len:
|
||||||
discard
|
evalTemplateAux(templ[i], actual, c, res)
|
||||||
else:
|
result.add res
|
||||||
var res = copyNode(c, templ, actual)
|
|
||||||
for i in 0..<templ.len:
|
|
||||||
evalTemplateAux(templ[i], actual, c, res)
|
|
||||||
result.add res
|
|
||||||
if isDeclarative: c.isDeclarative = false
|
if isDeclarative: c.isDeclarative = false
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -204,7 +200,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||||
result = copyNode(body)
|
result = copyNode(body)
|
||||||
ctx.instLines = sfCallsite in tmpl.flags
|
ctx.instLines = sfCallsite in tmpl.flags
|
||||||
if ctx.instLines:
|
if ctx.instLines:
|
||||||
setInfoRecursive(result, n.info)
|
result.info = n.info
|
||||||
for i in 0..<body.safeLen:
|
for i in 0..<body.safeLen:
|
||||||
evalTemplateAux(body[i], args, ctx, result)
|
evalTemplateAux(body[i], args, ctx, result)
|
||||||
result.flags.incl nfFromTemplate
|
result.flags.incl nfFromTemplate
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,9 @@
|
||||||
# from a lineinfos file, to provide generalized procedures to compile
|
# from a lineinfos file, to provide generalized procedures to compile
|
||||||
# nim files.
|
# nim files.
|
||||||
|
|
||||||
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
|
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils
|
||||||
|
|
||||||
import std/[os, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
|
import os, strutils, osproc, std/sha1, streams, sequtils, times, strtabs, json
|
||||||
|
|
||||||
import std / strutils except addf
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/syncio
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TInfoCCProp* = enum # properties of the C compiler:
|
TInfoCCProp* = enum # properties of the C compiler:
|
||||||
|
|
@ -88,10 +83,10 @@ compiler gcc:
|
||||||
linkLibCmd: " -l$1",
|
linkLibCmd: " -l$1",
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "-fPIC",
|
pic: "-fPIC",
|
||||||
asmStmtFrmt: "__asm__($1);$n",
|
asmStmtFrmt: "asm($1);$n",
|
||||||
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||||
produceAsm: gnuAsmListing,
|
produceAsm: gnuAsmListing,
|
||||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
cppXsupport: "-std=gnu++14 -funsigned-char",
|
||||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||||
hasAttribute})
|
hasAttribute})
|
||||||
|
|
||||||
|
|
@ -118,7 +113,7 @@ compiler nintendoSwitchGCC:
|
||||||
asmStmtFrmt: "asm($1);$n",
|
asmStmtFrmt: "asm($1);$n",
|
||||||
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||||
produceAsm: gnuAsmListing,
|
produceAsm: gnuAsmListing,
|
||||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
cppXsupport: "-std=gnu++14 -funsigned-char",
|
||||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||||
hasAttribute})
|
hasAttribute})
|
||||||
|
|
||||||
|
|
@ -155,7 +150,7 @@ compiler vcc:
|
||||||
compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file",
|
compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file",
|
||||||
buildGui: " /SUBSYSTEM:WINDOWS user32.lib ",
|
buildGui: " /SUBSYSTEM:WINDOWS user32.lib ",
|
||||||
buildDll: " /LD",
|
buildDll: " /LD",
|
||||||
buildLib: "vccexe --command:lib$vccplatform /nologo /OUT:$libfile $objfiles",
|
buildLib: "lib /OUT:$libfile $objfiles",
|
||||||
linkerExe: "cl",
|
linkerExe: "cl",
|
||||||
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
|
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
|
||||||
includeCmd: " /I",
|
includeCmd: " /I",
|
||||||
|
|
@ -257,7 +252,7 @@ compiler envcc:
|
||||||
buildGui: "",
|
buildGui: "",
|
||||||
buildDll: " -shared ",
|
buildDll: " -shared ",
|
||||||
buildLib: "", # XXX: not supported yet
|
buildLib: "", # XXX: not supported yet
|
||||||
linkerExe: "",
|
linkerExe: "cc",
|
||||||
linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
|
linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
|
||||||
includeCmd: " -I",
|
includeCmd: " -I",
|
||||||
linkDirCmd: "", # XXX: not supported yet
|
linkDirCmd: "", # XXX: not supported yet
|
||||||
|
|
@ -328,9 +323,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
|
||||||
platform.OS[conf.target.targetOS].name & '.' &
|
platform.OS[conf.target.targetOS].name & '.' &
|
||||||
CC[c].name & fullSuffix
|
CC[c].name & fullSuffix
|
||||||
result = getConfigVar(conf, fullCCname)
|
result = getConfigVar(conf, fullCCname)
|
||||||
if existsConfigVar(conf, fullCCname):
|
if result.len == 0:
|
||||||
result = getConfigVar(conf, fullCCname)
|
|
||||||
else:
|
|
||||||
# not overridden for this cross compilation setting?
|
# not overridden for this cross compilation setting?
|
||||||
result = getConfigVar(conf, CC[c].name & fullSuffix)
|
result = getConfigVar(conf, CC[c].name & fullSuffix)
|
||||||
else:
|
else:
|
||||||
|
|
@ -374,7 +367,6 @@ proc initVars*(conf: ConfigRef) =
|
||||||
|
|
||||||
proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile,
|
proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile,
|
||||||
createSubDir: bool = true): AbsoluteFile =
|
createSubDir: bool = true): AbsoluteFile =
|
||||||
## Generate the absolute file path to the generated modules.
|
|
||||||
result = completeGeneratedFilePath(conf, cfile, createSubDir)
|
result = completeGeneratedFilePath(conf, cfile, createSubDir)
|
||||||
|
|
||||||
proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
|
proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
|
||||||
|
|
@ -385,7 +377,7 @@ proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
|
||||||
conf.toCompile.add(cf)
|
conf.toCompile.add(cf)
|
||||||
|
|
||||||
proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) =
|
proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) =
|
||||||
let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string
|
let key = completeCfilePath(conf, withPackageName(conf, nimfile)).string
|
||||||
var value = conf.cfileSpecificOptions.getOrDefault(key)
|
var value = conf.cfileSpecificOptions.getOrDefault(key)
|
||||||
if strutils.find(value, option, 0) < 0:
|
if strutils.find(value, option, 0) < 0:
|
||||||
addOpt(value, option)
|
addOpt(value, option)
|
||||||
|
|
@ -438,13 +430,7 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
|
||||||
# really: Cross compilation from Linux to Linux for example is entirely
|
# really: Cross compilation from Linux to Linux for example is entirely
|
||||||
# reasonable.
|
# reasonable.
|
||||||
# `optGenMapping` is included here for niminst.
|
# `optGenMapping` is included here for niminst.
|
||||||
# We use absolute paths for vcc / cl, see issue #19883.
|
result = conf.globalOptions * {optGenScript, optGenMapping} != {}
|
||||||
let options =
|
|
||||||
if conf.cCompiler == ccVcc:
|
|
||||||
{optGenMapping}
|
|
||||||
else:
|
|
||||||
{optGenScript, optGenMapping}
|
|
||||||
result = conf.globalOptions * options != {}
|
|
||||||
|
|
||||||
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
|
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
|
||||||
result = conf.compileOptions
|
result = conf.compileOptions
|
||||||
|
|
@ -497,10 +483,7 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} =
|
||||||
(conf.target.hostOS == osWindows)
|
(conf.target.hostOS == osWindows)
|
||||||
|
|
||||||
proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool =
|
proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool =
|
||||||
# List of possible file extensions taken from gcc
|
conf.backend == backendCpp and not cfile.string.endsWith(".c")
|
||||||
for ext in [".C", ".cc", ".cpp", ".CPP", ".c++", ".cp", ".cxx"]:
|
|
||||||
if cfile.string.endsWith(ext): return true
|
|
||||||
false
|
|
||||||
|
|
||||||
proc envFlags(conf: ConfigRef): string =
|
proc envFlags(conf: ConfigRef): string =
|
||||||
result = if conf.backend == backendCpp:
|
result = if conf.backend == backendCpp:
|
||||||
|
|
@ -508,14 +491,14 @@ proc envFlags(conf: ConfigRef): string =
|
||||||
else:
|
else:
|
||||||
getEnv("CFLAGS")
|
getEnv("CFLAGS")
|
||||||
|
|
||||||
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string =
|
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string =
|
||||||
if compiler == ccEnv:
|
if compiler == ccEnv:
|
||||||
result = if isCpp:
|
result = if useCpp(conf, cfile):
|
||||||
getEnv("CXX")
|
getEnv("CXX")
|
||||||
else:
|
else:
|
||||||
getEnv("CC")
|
getEnv("CC")
|
||||||
else:
|
else:
|
||||||
result = if isCpp:
|
result = if useCpp(conf, cfile):
|
||||||
CC[compiler].cppCompiler
|
CC[compiler].cppCompiler
|
||||||
else:
|
else:
|
||||||
CC[compiler].compilerExe
|
CC[compiler].compilerExe
|
||||||
|
|
@ -529,36 +512,47 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool =
|
||||||
result = false # assume an old or crappy GCC
|
result = false # assume an old or crappy GCC
|
||||||
var exe = getConfigVar(conf, conf.cCompiler, ".exe")
|
var exe = getConfigVar(conf, conf.cCompiler, ".exe")
|
||||||
if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
|
if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
|
||||||
# NOTE: should we need the full version, use -dumpfullversion
|
let (s, exitCode) = try: execCmdEx(exe & " --version") except: ("", 1)
|
||||||
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
|
|
||||||
if exitCode == 0:
|
if exitCode == 0:
|
||||||
var major: int
|
var i = 0
|
||||||
discard parseInt(s, major)
|
var j = 0
|
||||||
result = major >= 5
|
# the version is the last part of the first line:
|
||||||
|
while i < s.len and s[i] != '\n':
|
||||||
|
if s[i] in {' ', '\t'}: j = i+1
|
||||||
|
inc i
|
||||||
|
if j > 0:
|
||||||
|
var major = 0
|
||||||
|
while j < s.len and s[j] in {'0'..'9'}:
|
||||||
|
major = major * 10 + (ord(s[j]) - ord('0'))
|
||||||
|
inc j
|
||||||
|
if i < s.len and s[j] == '.': inc j
|
||||||
|
while j < s.len and s[j] in {'0'..'9'}:
|
||||||
|
inc j
|
||||||
|
if j+1 < s.len and s[j] == '.' and s[j+1] in {'0'..'9'}:
|
||||||
|
# we found a third version number, chances are high
|
||||||
|
# we really parsed the version:
|
||||||
|
result = major >= 5
|
||||||
else:
|
else:
|
||||||
result = conf.cCompiler == ccCLang
|
result = conf.cCompiler == ccCLang
|
||||||
|
|
||||||
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
|
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
|
||||||
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
|
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
|
||||||
else: getCompilerExe(conf, compiler, optMixedMode in conf.globalOptions or conf.backend == backendCpp)
|
elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler
|
||||||
|
else: getCompilerExe(conf, compiler, AbsoluteFile"")
|
||||||
|
|
||||||
proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||||
isMainFile = false; produceOutput = false): string =
|
isMainFile = false; produceOutput = false): string =
|
||||||
let
|
let c = conf.cCompiler
|
||||||
c = conf.cCompiler
|
|
||||||
isCpp = useCpp(conf, cfile.cname)
|
|
||||||
# We produce files like module.nim.cpp, so the absolute Nim filename is not
|
# We produce files like module.nim.cpp, so the absolute Nim filename is not
|
||||||
# cfile.name but `cfile.cname.changeFileExt("")`:
|
# cfile.name but `cfile.cname.changeFileExt("")`:
|
||||||
var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string)
|
var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string)
|
||||||
if isCpp:
|
if useCpp(conf, cfile.cname):
|
||||||
# needs to be prepended so that --passc:-std=c++17 can override default.
|
# needs to be prepended so that --passc:-std=c++17 can override default.
|
||||||
# we could avoid allocation by making cFileSpecificOptions inplace
|
# we could avoid allocation by making cFileSpecificOptions inplace
|
||||||
options = CC[c].cppXsupport & ' ' & options
|
options = CC[c].cppXsupport & ' ' & options
|
||||||
# If any C++ file was compiled, we need to use C++ driver for linking as well
|
|
||||||
incl conf.globalOptions, optMixedMode
|
|
||||||
|
|
||||||
var exe = getConfigVar(conf, c, ".exe")
|
var exe = getConfigVar(conf, c, ".exe")
|
||||||
if exe.len == 0: exe = getCompilerExe(conf, c, isCpp)
|
if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
|
||||||
|
|
||||||
if needsExeExt(conf): exe = addFileExt(exe, "exe")
|
if needsExeExt(conf): exe = addFileExt(exe, "exe")
|
||||||
if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and
|
if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and
|
||||||
|
|
@ -578,7 +572,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||||
|
|
||||||
compilePattern = joinPath(conf.cCompilerPath, exe)
|
compilePattern = joinPath(conf.cCompilerPath, exe)
|
||||||
else:
|
else:
|
||||||
compilePattern = getCompilerExe(conf, c, isCpp)
|
compilePattern = getCompilerExe(conf, c, cfile.cname)
|
||||||
|
|
||||||
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
|
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
|
||||||
|
|
||||||
|
|
@ -620,7 +614,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||||
"for the selected C compiler: " & CC[conf.cCompiler].name)
|
"for the selected C compiler: " & CC[conf.cCompiler].name)
|
||||||
|
|
||||||
result.add(' ')
|
result.add(' ')
|
||||||
strutils.addf(result, CC[c].compileTmpl, [
|
result.addf(CC[c].compileTmpl, [
|
||||||
"dfile", dfile,
|
"dfile", dfile,
|
||||||
"file", cfsh, "objfile", quoteShell(objfile),
|
"file", cfsh, "objfile", quoteShell(objfile),
|
||||||
"options", options, "include", includeCmd,
|
"options", options, "include", includeCmd,
|
||||||
|
|
@ -640,7 +634,7 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
|
||||||
proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
||||||
if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good
|
if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good
|
||||||
|
|
||||||
let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
|
let hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
|
||||||
let currentHash = footprint(conf, cfile)
|
let currentHash = footprint(conf, cfile)
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, hashFile.string, fmRead):
|
if open(f, hashFile.string, fmRead):
|
||||||
|
|
@ -655,10 +649,8 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
||||||
close(f)
|
close(f)
|
||||||
|
|
||||||
proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
|
proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
|
||||||
# we want to generate the hash file unconditionally
|
|
||||||
let extFileChanged = externalFileChanged(conf, c)
|
|
||||||
if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
|
if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
|
||||||
not extFileChanged:
|
not externalFileChanged(conf, c):
|
||||||
c.flags.incl CfileFlag.Cached
|
c.flags.incl CfileFlag.Cached
|
||||||
else:
|
else:
|
||||||
# make sure Nim keeps recompiling the external file on reruns
|
# make sure Nim keeps recompiling the external file on reruns
|
||||||
|
|
@ -673,13 +665,11 @@ proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
|
||||||
addExternalFileToCompile(conf, c)
|
addExternalFileToCompile(conf, c)
|
||||||
|
|
||||||
proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||||
objfiles: string, isDllBuild: bool, removeStaticFile: bool): string =
|
objfiles: string, isDllBuild: bool): string =
|
||||||
if optGenStaticLib in conf.globalOptions:
|
if optGenStaticLib in conf.globalOptions:
|
||||||
if removeStaticFile:
|
removeFile output # fixes: bug #16947
|
||||||
removeFile output # fixes: bug #16947
|
|
||||||
result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
|
result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
|
||||||
"objfiles", objfiles,
|
"objfiles", objfiles]
|
||||||
"vccplatform", vccplatform(conf)]
|
|
||||||
else:
|
else:
|
||||||
var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
|
var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
|
||||||
if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
|
if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
|
||||||
|
|
@ -712,7 +702,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||||
"exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
|
"exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
|
||||||
result.add ' '
|
result.add ' '
|
||||||
strutils.addf(result, linkTmpl, ["builddll", builddll,
|
result.addf(linkTmpl, ["builddll", builddll,
|
||||||
"mapfile", mapfile,
|
"mapfile", mapfile,
|
||||||
"buildgui", buildgui, "options", linkOptions,
|
"buildgui", buildgui, "options", linkOptions,
|
||||||
"objfiles", objfiles, "exefile", exefile,
|
"objfiles", objfiles, "exefile", exefile,
|
||||||
|
|
@ -760,9 +750,8 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||||
if optCDebug in conf.globalOptions and conf.cCompiler == ccVcc:
|
if optCDebug in conf.globalOptions and conf.cCompiler == ccVcc:
|
||||||
result.add " /Zi /FS /Od"
|
result.add " /Zi /FS /Od"
|
||||||
|
|
||||||
template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string,
|
template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string): string =
|
||||||
removeStaticFile = false): string =
|
getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions)
|
||||||
getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions, removeStaticFile)
|
|
||||||
|
|
||||||
template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) =
|
template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) =
|
||||||
try:
|
try:
|
||||||
|
|
@ -836,15 +825,6 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
|
||||||
finally:
|
finally:
|
||||||
removeFile(linkerArgs)
|
removeFile(linkerArgs)
|
||||||
|
|
||||||
proc linkViaShellScript(conf: ConfigRef; cmd: string) =
|
|
||||||
let linkerScript = conf.projectName & "_" & "linkerScript.sh"
|
|
||||||
writeFile(linkerScript, cmd)
|
|
||||||
let shell = getEnv("SHELL")
|
|
||||||
try:
|
|
||||||
execLinkCmd(conf, shell & " " & linkerScript)
|
|
||||||
finally:
|
|
||||||
removeFile(linkerScript)
|
|
||||||
|
|
||||||
proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
|
proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
|
||||||
if noAbsolutePaths(conf): f.obj.extractFilename
|
if noAbsolutePaths(conf): f.obj.extractFilename
|
||||||
else: f.obj.string
|
else: f.obj.string
|
||||||
|
|
@ -858,23 +838,9 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
|
||||||
proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
|
proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
|
||||||
if conf.hasHint(hintCC):
|
if conf.hasHint(hintCC):
|
||||||
if optListCmd in conf.globalOptions or conf.verbosity > 1:
|
if optListCmd in conf.globalOptions or conf.verbosity > 1:
|
||||||
result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd)
|
result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd)
|
||||||
else:
|
else:
|
||||||
result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name)
|
result = MsgKindToStr[hintCC] % demanglePackageName(path.splitFile.name)
|
||||||
|
|
||||||
proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) =
|
|
||||||
# Prevent linkcmd from exceeding the maximum command line length.
|
|
||||||
# Windows's command line limit is about 8K (8191 characters) so C compilers on
|
|
||||||
# Windows support a feature where the command line can be passed via ``@linkcmd``
|
|
||||||
# to them.
|
|
||||||
const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000
|
|
||||||
if linkCmd.len > MaxCmdLen:
|
|
||||||
when defined(macosx):
|
|
||||||
linkViaShellScript(conf, linkCmd)
|
|
||||||
else:
|
|
||||||
linkViaResponseFile(conf, linkCmd)
|
|
||||||
else:
|
|
||||||
execLinkCmd(conf, linkCmd)
|
|
||||||
|
|
||||||
proc callCCompiler*(conf: ConfigRef) =
|
proc callCCompiler*(conf: ConfigRef) =
|
||||||
var
|
var
|
||||||
|
|
@ -884,7 +850,7 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||||
return # speed up that call if only compiling and no script shall be
|
return # speed up that call if only compiling and no script shall be
|
||||||
# generated
|
# generated
|
||||||
#var c = cCompiler
|
#var c = cCompiler
|
||||||
var script: Rope = ""
|
var script: Rope = nil
|
||||||
var cmds: TStringSeq
|
var cmds: TStringSeq
|
||||||
var prettyCmds: TStringSeq
|
var prettyCmds: TStringSeq
|
||||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||||
|
|
@ -925,7 +891,7 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||||
let objFile = conf.getObjFilePath(x)
|
let objFile = conf.getObjFilePath(x)
|
||||||
let buildDll = idx != mainFileIdx
|
let buildDll = idx != mainFileIdx
|
||||||
let linkTarget = conf.hcrLinkTargetName(objFile, not buildDll)
|
let linkTarget = conf.hcrLinkTargetName(objFile, not buildDll)
|
||||||
cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll, removeStaticFile = true))
|
cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll))
|
||||||
# try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache
|
# try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache
|
||||||
# for more info check the comment inside of getLinkCmd() where the /PDB:<filename> MSVC flag is used
|
# for more info check the comment inside of getLinkCmd() where the /PDB:<filename> MSVC flag is used
|
||||||
if isVSCompatible(conf):
|
if isVSCompatible(conf):
|
||||||
|
|
@ -947,12 +913,18 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||||
objfiles.add(' ')
|
objfiles.add(' ')
|
||||||
objfiles.add(quoteShell(objFile))
|
objfiles.add(quoteShell(objFile))
|
||||||
let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput
|
let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput
|
||||||
else: AbsoluteFile(conf.outFile)
|
else: AbsoluteFile(conf.projectName)
|
||||||
|
linkCmd = getLinkCmd(conf, mainOutput, objfiles)
|
||||||
linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true)
|
|
||||||
extraCmds = getExtraCmds(conf, mainOutput)
|
extraCmds = getExtraCmds(conf, mainOutput)
|
||||||
if optCompileOnly notin conf.globalOptions:
|
if optCompileOnly notin conf.globalOptions:
|
||||||
preventLinkCmdMaxCmdLen(conf, linkCmd)
|
const MaxCmdLen = when defined(windows): 8_000 else: 32_000
|
||||||
|
if linkCmd.len > MaxCmdLen:
|
||||||
|
# Windows's command line limit is about 8K (don't laugh...) so C compilers on
|
||||||
|
# Windows support a feature where the command line can be passed via ``@linkcmd``
|
||||||
|
# to them.
|
||||||
|
linkViaResponseFile(conf, linkCmd)
|
||||||
|
else:
|
||||||
|
execLinkCmd(conf, linkCmd)
|
||||||
for cmd in extraCmds:
|
for cmd in extraCmds:
|
||||||
execExternalProgram(conf, cmd, hintExecuting)
|
execExternalProgram(conf, cmd, hintExecuting)
|
||||||
else:
|
else:
|
||||||
|
|
@ -970,91 +942,195 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
|
||||||
# works out of the box with `hashMainCompilationParams`.
|
# works out of the box with `hashMainCompilationParams`.
|
||||||
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
|
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
|
||||||
|
|
||||||
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
|
|
||||||
type BuildCache = object
|
|
||||||
cacheVersion: string
|
|
||||||
outputFile: string
|
|
||||||
compile: seq[(string, string)]
|
|
||||||
link: seq[string]
|
|
||||||
linkcmd: string
|
|
||||||
extraCmds: seq[string]
|
|
||||||
configFiles: seq[string] # the hash shouldn't be needed
|
|
||||||
stdinInput: bool
|
|
||||||
projectIsCmd: bool
|
|
||||||
cmdInput: string
|
|
||||||
currentDir: string
|
|
||||||
cmdline: string
|
|
||||||
depfiles: seq[(string, string)]
|
|
||||||
nimexe: string
|
|
||||||
|
|
||||||
proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||||
var linkFiles = collect(for it in conf.externalToLink:
|
# xxx use std/json instead, will result in simpler, more maintainable code.
|
||||||
var it = it
|
template lit(x: string) = f.write x
|
||||||
if conf.noAbsolutePaths: it = it.extractFilename
|
template str(x: string) =
|
||||||
it.addFileExt(CC[conf.cCompiler].objExt))
|
buf.setLen 0
|
||||||
for it in conf.toCompile: linkFiles.add it.obj.string
|
escapeJson(x, buf)
|
||||||
var bcache = BuildCache(
|
f.write buf
|
||||||
cacheVersion: cacheVersion,
|
|
||||||
outputFile: conf.absOutFile.string,
|
proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
|
||||||
compile: collect(for i, it in conf.toCompile:
|
var comma = false
|
||||||
if CfileFlag.Cached notin it.flags: (it.cname.string, getCompileCFileCmd(conf, it))),
|
for i, it in clist:
|
||||||
link: linkFiles,
|
if CfileFlag.Cached in it.flags: continue
|
||||||
linkcmd: getLinkCmd(conf, conf.absOutFile, linkFiles.quoteShellCommand),
|
let compileCmd = getCompileCFileCmd(conf, it)
|
||||||
extraCmds: getExtraCmds(conf, conf.absOutFile),
|
if comma: lit ",\L" else: comma = true
|
||||||
stdinInput: conf.projectIsStdin,
|
lit "["
|
||||||
projectIsCmd: conf.projectIsCmd,
|
str it.cname.string
|
||||||
cmdInput: conf.cmdInput,
|
lit ", "
|
||||||
configFiles: conf.configFiles.mapIt(it.string),
|
str compileCmd
|
||||||
currentDir: getCurrentDir())
|
lit "]"
|
||||||
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
|
|
||||||
bcache.cmdline = conf.commandLine
|
proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList;
|
||||||
bcache.depfiles = collect(for it in conf.m.fileInfos:
|
llist: seq[string]) =
|
||||||
|
var pastStart = false
|
||||||
|
template impl(path) =
|
||||||
|
let path2 = quoteShell(path)
|
||||||
|
objfiles.add(' ')
|
||||||
|
objfiles.add(path2)
|
||||||
|
if pastStart: lit ",\L"
|
||||||
|
str path2
|
||||||
|
pastStart = true
|
||||||
|
for it in llist:
|
||||||
|
let objfile = if noAbsolutePaths(conf): it.extractFilename else: it
|
||||||
|
impl(addFileExt(objfile, CC[conf.cCompiler].objExt))
|
||||||
|
for it in clist:
|
||||||
|
impl(it.obj)
|
||||||
|
lit "\L"
|
||||||
|
|
||||||
|
proc depfiles(conf: ConfigRef; f: File; buf: var string) =
|
||||||
|
var i = 0
|
||||||
|
for it in conf.m.fileInfos:
|
||||||
let path = it.fullPath.string
|
let path = it.fullPath.string
|
||||||
if isAbsolute(path): # TODO: else?
|
if isAbsolute(path): # TODO: else?
|
||||||
(path, $secureHashFile(path)))
|
if i > 0: lit "],\L"
|
||||||
bcache.nimexe = hashNimExe()
|
lit "["
|
||||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
str path
|
||||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
lit ", "
|
||||||
|
str $secureHashFile(path)
|
||||||
|
inc i
|
||||||
|
lit "]\L"
|
||||||
|
|
||||||
|
|
||||||
|
var buf = newStringOfCap(50)
|
||||||
|
let jsonFile = conf.jsonBuildInstructionsFile
|
||||||
|
conf.jsonBuildFile = jsonFile
|
||||||
|
let output = conf.absOutFile
|
||||||
|
|
||||||
|
var f: File
|
||||||
|
if open(f, jsonFile.string, fmWrite):
|
||||||
|
lit "{\L"
|
||||||
|
lit "\"outputFile\": "
|
||||||
|
str $output
|
||||||
|
|
||||||
|
lit ",\L\"compile\":[\L"
|
||||||
|
cfiles(conf, f, buf, conf.toCompile, false)
|
||||||
|
lit "],\L\"link\":[\L"
|
||||||
|
var objfiles = ""
|
||||||
|
# XXX add every file here that is to link
|
||||||
|
linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink)
|
||||||
|
|
||||||
|
lit "],\L\"linkcmd\": "
|
||||||
|
str getLinkCmd(conf, output, objfiles)
|
||||||
|
|
||||||
|
lit ",\L\"extraCmds\": "
|
||||||
|
lit $(%* getExtraCmds(conf, conf.absOutFile))
|
||||||
|
|
||||||
|
lit ",\L\"stdinInput\": "
|
||||||
|
lit $(%* conf.projectIsStdin)
|
||||||
|
lit ",\L\"projectIsCmd\": "
|
||||||
|
lit $(%* conf.projectIsCmd)
|
||||||
|
lit ",\L\"cmdInput\": "
|
||||||
|
lit $(%* conf.cmdInput)
|
||||||
|
lit ",\L\"currentDir\": "
|
||||||
|
lit $(%* getCurrentDir())
|
||||||
|
|
||||||
|
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
|
||||||
|
lit ",\L\"cmdline\": "
|
||||||
|
str conf.commandLine
|
||||||
|
lit ",\L\"depfiles\":[\L"
|
||||||
|
depfiles(conf, f, buf)
|
||||||
|
lit "],\L\"nimexe\": \L"
|
||||||
|
str hashNimExe()
|
||||||
|
lit "\L"
|
||||||
|
|
||||||
|
lit "\L}\L"
|
||||||
|
close(f)
|
||||||
|
|
||||||
proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool =
|
proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool =
|
||||||
if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true
|
if not fileExists(jsonFile): return true
|
||||||
var bcache: BuildCache
|
if not fileExists(conf.absOutFile): return true
|
||||||
try: bcache.fromJson(jsonFile.string.parseFile)
|
result = false
|
||||||
|
try:
|
||||||
|
let data = json.parseFile(jsonFile.string)
|
||||||
|
for key in "depfiles cmdline stdinInput currentDir".split:
|
||||||
|
if not data.hasKey(key): return true
|
||||||
|
if getCurrentDir() != data["currentDir"].getStr:
|
||||||
|
# fixes bug #16271
|
||||||
|
# Note that simply comparing `expandFilename(projectFile)` would
|
||||||
|
# not be sufficient in case other flags depend implicitly on `getCurrentDir`,
|
||||||
|
# and would require much more care. Simply re-compiling is safer for now.
|
||||||
|
# A better strategy for future work would be to cache (with an LRU cache)
|
||||||
|
# the N most recent unique build instructions, as done with `rdmd`,
|
||||||
|
# which is both robust and avoids recompilation when switching back and forth
|
||||||
|
# between projects, see https://github.com/timotheecour/Nim/issues/199
|
||||||
|
return true
|
||||||
|
let oldCmdLine = data["cmdline"].getStr
|
||||||
|
if conf.commandLine != oldCmdLine:
|
||||||
|
return true
|
||||||
|
if hashNimExe() != data["nimexe"].getStr:
|
||||||
|
return true
|
||||||
|
let stdinInput = data["stdinInput"].getBool
|
||||||
|
let projectIsCmd = data["projectIsCmd"].getBool
|
||||||
|
if conf.projectIsStdin or stdinInput:
|
||||||
|
# could optimize by returning false if stdin input was the same,
|
||||||
|
# but I'm not sure how to get full stding input
|
||||||
|
return true
|
||||||
|
|
||||||
|
if conf.projectIsCmd or projectIsCmd:
|
||||||
|
if not (conf.projectIsCmd and projectIsCmd): return true
|
||||||
|
if not data.hasKey("cmdInput"): return true
|
||||||
|
let cmdInput = data["cmdInput"].getStr
|
||||||
|
if cmdInput != conf.cmdInput: return true
|
||||||
|
|
||||||
|
let depfilesPairs = data["depfiles"]
|
||||||
|
doAssert depfilesPairs.kind == JArray
|
||||||
|
for p in depfilesPairs:
|
||||||
|
doAssert p.kind == JArray
|
||||||
|
# >= 2 for forwards compatibility with potential later .json files:
|
||||||
|
doAssert p.len >= 2
|
||||||
|
let depFilename = p[0].getStr
|
||||||
|
let oldHashValue = p[1].getStr
|
||||||
|
let newHashValue = $secureHashFile(depFilename)
|
||||||
|
if oldHashValue != newHashValue:
|
||||||
|
return true
|
||||||
except IOError, OSError, ValueError:
|
except IOError, OSError, ValueError:
|
||||||
stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string
|
echo "Warning: JSON processing failed: ", getCurrentExceptionMsg()
|
||||||
return true
|
result = true
|
||||||
if bcache.currentDir != getCurrentDir() or # fixes bug #16271
|
|
||||||
bcache.configFiles != conf.configFiles.mapIt(it.string) or
|
|
||||||
bcache.cacheVersion != cacheVersion or bcache.outputFile != conf.absOutFile.string or
|
|
||||||
bcache.cmdline != conf.commandLine or bcache.nimexe != hashNimExe() or
|
|
||||||
bcache.projectIsCmd != conf.projectIsCmd or conf.cmdInput != bcache.cmdInput: return true
|
|
||||||
if bcache.stdinInput or conf.projectIsStdin: return true
|
|
||||||
# xxx optimize by returning false if stdin input was the same
|
|
||||||
for (file, hash) in bcache.depfiles:
|
|
||||||
if $secureHashFile(file) != hash: return true
|
|
||||||
|
|
||||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||||
var bcache: BuildCache
|
try:
|
||||||
try: bcache.fromJson(jsonFile.string.parseFile)
|
let data = json.parseFile(jsonFile.string)
|
||||||
except ValueError, KeyError, JsonKindError:
|
let output = data["outputFile"].getStr
|
||||||
|
createDir output.parentDir
|
||||||
|
let outputCurrent = $conf.absOutFile
|
||||||
|
if output != outputCurrent:
|
||||||
|
# previously, any specified output file would be silently ignored;
|
||||||
|
# simply copying won't work in some cases, for example with `extraCmds`,
|
||||||
|
# so we just make it an error, user should use same command for jsonscript
|
||||||
|
# as was used with --compileOnly.
|
||||||
|
globalError(conf, gCmdLineInfo, "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string])
|
||||||
|
|
||||||
|
let toCompile = data["compile"]
|
||||||
|
doAssert toCompile.kind == JArray
|
||||||
|
var cmds: TStringSeq
|
||||||
|
var prettyCmds: TStringSeq
|
||||||
|
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||||
|
for c in toCompile:
|
||||||
|
doAssert c.kind == JArray
|
||||||
|
doAssert c.len >= 2
|
||||||
|
|
||||||
|
cmds.add(c[1].getStr)
|
||||||
|
prettyCmds.add displayProgressCC(conf, c[0].getStr, c[1].getStr)
|
||||||
|
|
||||||
|
execCmdsInParallel(conf, cmds, prettyCb)
|
||||||
|
|
||||||
|
let linkCmd = data["linkcmd"]
|
||||||
|
doAssert linkCmd.kind == JString
|
||||||
|
execLinkCmd(conf, linkCmd.getStr)
|
||||||
|
if data.hasKey("extraCmds"):
|
||||||
|
let extraCmds = data["extraCmds"]
|
||||||
|
doAssert extraCmds.kind == JArray
|
||||||
|
for cmd in extraCmds:
|
||||||
|
doAssert cmd.kind == JString, $cmd.kind
|
||||||
|
let cmd2 = cmd.getStr
|
||||||
|
execExternalProgram(conf, cmd2, hintExecuting)
|
||||||
|
|
||||||
|
except:
|
||||||
let e = getCurrentException()
|
let e = getCurrentException()
|
||||||
conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
|
conf.quitOrRaise "\ncaught exception:\n" & e.msg & "\nstacktrace:\n" & e.getStackTrace() &
|
||||||
[e.msg, e.getStackTrace(), jsonFile.string]
|
"error evaluating JSON file: " & jsonFile.string
|
||||||
let output = bcache.outputFile
|
|
||||||
createDir output.parentDir
|
|
||||||
let outputCurrent = $conf.absOutFile
|
|
||||||
if output != outputCurrent or bcache.cacheVersion != cacheVersion:
|
|
||||||
globalError(conf, gCmdLineInfo,
|
|
||||||
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
|
|
||||||
[outputCurrent, output, jsonFile.string])
|
|
||||||
var cmds, prettyCmds: TStringSeq
|
|
||||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
|
||||||
for (name, cmd) in bcache.compile:
|
|
||||||
cmds.add cmd
|
|
||||||
prettyCmds.add displayProgressCC(conf, name, cmd)
|
|
||||||
execCmdsInParallel(conf, cmds, prettyCb)
|
|
||||||
preventLinkCmdMaxCmdLen(conf, bcache.linkcmd)
|
|
||||||
for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting)
|
|
||||||
|
|
||||||
proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
|
proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
|
||||||
for it in list:
|
for it in list:
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type
|
||||||
info: TLineInfo
|
info: TLineInfo
|
||||||
indent, emitPar: int
|
indent, emitPar: int
|
||||||
x: string # the current input line
|
x: string # the current input line
|
||||||
outp: PLLStream # the output will be parsed by parser
|
outp: PLLStream # the output will be parsed by pnimsyn
|
||||||
subsChar, nimDirective: char
|
subsChar, nimDirective: char
|
||||||
emit, conc, toStr: string
|
emit, conc, toStr: string
|
||||||
curly, bracket, par: int
|
curly, bracket, par: int
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
import msgs, std / sha1, os, osproc, streams, options,
|
import msgs, std / sha1, os, osproc, streams, options,
|
||||||
lineinfos, pathutils
|
lineinfos, pathutils
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/syncio
|
|
||||||
|
|
||||||
proc readOutput(p: Process): (string, int) =
|
proc readOutput(p: Process): (string, int) =
|
||||||
result[0] = ""
|
result[0] = ""
|
||||||
var output = p.outputStream
|
var output = p.outputStream
|
||||||
|
|
@ -49,11 +46,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||||
if result[1] == 0:
|
if result[1] == 0:
|
||||||
writeFile(filename, result[0])
|
writeFile(filename, result[0])
|
||||||
except IOError, OSError:
|
except IOError, OSError:
|
||||||
if not readSuccessful:
|
if not readSuccessful: result = ("", -1)
|
||||||
when defined(nimLegacyGorgeErrors):
|
|
||||||
result = ("", -1)
|
|
||||||
else:
|
|
||||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
var p = startProcess(cmd, workingDir,
|
var p = startProcess(cmd, workingDir,
|
||||||
|
|
@ -64,7 +57,4 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||||
result = p.readOutput
|
result = p.readOutput
|
||||||
p.close()
|
p.close()
|
||||||
except IOError, OSError:
|
except IOError, OSError:
|
||||||
when defined(nimLegacyGorgeErrors):
|
result = ("", -1)
|
||||||
result = ("", -1)
|
|
||||||
else:
|
|
||||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
||||||
saturate, modulegraphs, options, lineinfos, int128
|
saturate, modulegraphs, options, lineinfos, int128
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
const
|
const
|
||||||
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||||
mEqStr, mEqSet, mEqCString}
|
mEqStr, mEqSet, mEqCString}
|
||||||
|
|
@ -68,12 +65,9 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
|
of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
|
||||||
n = n[0]
|
n = n[0]
|
||||||
of nkDerefExpr:
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
n = n[0]
|
n = n[0]
|
||||||
inc derefs
|
inc derefs
|
||||||
of nkHiddenDeref:
|
|
||||||
n = n[0]
|
|
||||||
if not isApprox: inc derefs
|
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
|
if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
|
||||||
n = n[0]
|
n = n[0]
|
||||||
|
|
@ -203,7 +197,7 @@ proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
|
||||||
nkIntLit.newIntNode(lastOrd(conf, typ))
|
nkIntLit.newIntNode(lastOrd(conf, typ))
|
||||||
elif typ.kind == tySequence and x.kind == nkSym and
|
elif typ.kind == tySequence and x.kind == nkSym and
|
||||||
x.sym.kind == skConst:
|
x.sym.kind == skConst:
|
||||||
nkIntLit.newIntNode(x.sym.astdef.len-1)
|
nkIntLit.newIntNode(x.sym.ast.len-1)
|
||||||
else:
|
else:
|
||||||
o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
|
o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
|
||||||
result.info = x.info
|
result.info = x.info
|
||||||
|
|
@ -447,14 +441,8 @@ proc sameTree*(a, b: PNode): bool =
|
||||||
proc hasSubTree(n, x: PNode): bool =
|
proc hasSubTree(n, x: PNode): bool =
|
||||||
if n.sameTree(x): result = true
|
if n.sameTree(x): result = true
|
||||||
else:
|
else:
|
||||||
case n.kind
|
for i in 0..n.safeLen-1:
|
||||||
of nkEmpty..nkNilLit:
|
if hasSubTree(n[i], x): return true
|
||||||
result = n.sameTree(x)
|
|
||||||
of nkFormalParams:
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
if hasSubTree(n[i], x): return true
|
|
||||||
|
|
||||||
proc invalidateFacts*(s: var seq[PNode], n: PNode) =
|
proc invalidateFacts*(s: var seq[PNode], n: PNode) =
|
||||||
# We are able to guard local vars (as opposed to 'let' variables)!
|
# We are able to guard local vars (as opposed to 'let' variables)!
|
||||||
|
|
@ -1057,12 +1045,8 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||||
assert check.getMagic == mNot
|
assert check.getMagic == mNot
|
||||||
result = buildProperFieldCheck(access, check[1], o).neg(o)
|
result = buildProperFieldCheck(access, check[1], o).neg(o)
|
||||||
|
|
||||||
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef; produceError: bool) =
|
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
|
let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
|
||||||
if check != nil and m.doesImply(check) != impYes:
|
if check != nil and m.doesImply(check) != impYes:
|
||||||
if produceError:
|
message(conf, n.info, warnProveField, renderTree(n[0])); break
|
||||||
localError(conf, n.info, "field access outside of valid case branch: " & renderTree(n[0]))
|
|
||||||
else:
|
|
||||||
message(conf, n.info, warnProveField, renderTree(n[0]))
|
|
||||||
break
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# This include implements the high level optimization pass.
|
# This include implements the high level optimization pass.
|
||||||
# included from sem.nim
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc hlo(c: PContext, n: PNode): PNode
|
proc hlo(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
|
|
@ -71,7 +67,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||||
# already processed (special cases in semstmts.nim)
|
# already processed (special cases in semstmts.nim)
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
|
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
|
||||||
n[0].kind == nkSym and
|
n[0].kind == nkSym and
|
||||||
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
||||||
# do not optimize 'var g {.global} = re(...)' again!
|
# do not optimize 'var g {.global} = re(...)' again!
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
## A BiTable is a table that can be seen as an optimized pair
|
## A BiTable is a table that can be seen as an optimized pair
|
||||||
## of `(Table[LitId, Val], Table[Val, LitId])`.
|
## of (Table[LitId, Val], Table[Val, LitId]).
|
||||||
|
|
||||||
import hashes, rodfiles
|
import hashes, rodfiles
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
LitId* = distinct uint32
|
LitId* = distinct uint32
|
||||||
|
|
||||||
|
|
@ -93,13 +90,13 @@ proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
|
||||||
t.vals.add v
|
t.vals.add v
|
||||||
|
|
||||||
|
|
||||||
proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} =
|
proc `[]`*[T](t: var BiTable[T]; LitId: LitId): var T {.inline.} =
|
||||||
let idx = idToIdx litId
|
let idx = idToIdx LitId
|
||||||
assert idx < t.vals.len
|
assert idx < t.vals.len
|
||||||
result = t.vals[idx]
|
result = t.vals[idx]
|
||||||
|
|
||||||
proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} =
|
proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} =
|
||||||
let idx = idToIdx litId
|
let idx = idToIdx LitId
|
||||||
assert idx < t.vals.len
|
assert idx < t.vals.len
|
||||||
result = t.vals[idx]
|
result = t.vals[idx]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,8 @@
|
||||||
|
|
||||||
import std/packedsets, algorithm, tables
|
import std/packedsets, algorithm, tables
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||||
pathutils, extccomp, msgs, modulepaths]
|
pathutils, extccomp, msgs]
|
||||||
|
|
||||||
import packed_ast, ic, dce, rodfiles
|
import packed_ast, ic, dce, rodfiles
|
||||||
|
|
||||||
|
|
@ -64,8 +61,7 @@ proc addFileToLink(config: ConfigRef; m: PSym) =
|
||||||
if config.backend == backendCpp: ".nim.cpp"
|
if config.backend == backendCpp: ".nim.cpp"
|
||||||
elif config.backend == backendObjc: ".nim.m"
|
elif config.backend == backendObjc: ".nim.m"
|
||||||
else: ".nim.c"
|
else: ".nim.c"
|
||||||
let cfile = changeFileExt(completeCfilePath(config,
|
let cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext)
|
||||||
mangleModuleName(config, filename).AbsoluteFile), ext)
|
|
||||||
let objFile = completeCfilePath(config, toObjFile(config, cfile))
|
let objFile = completeCfilePath(config, toObjFile(config, cfile))
|
||||||
if fileExists(objFile):
|
if fileExists(objFile):
|
||||||
var cf = Cfile(nimname: m.name.s, cname: cfile,
|
var cf = Cfile(nimname: m.name.s, cname: cfile,
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@
|
||||||
## Dead code elimination (=DCE) for IC.
|
## Dead code elimination (=DCE) for IC.
|
||||||
|
|
||||||
import std/[intsets, tables]
|
import std/[intsets, tables]
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".." / [ast, options, lineinfos, types]
|
import ".." / [ast, options, lineinfos, types]
|
||||||
|
|
||||||
import packed_ast, ic, bitabs
|
import packed_ast, ic, bitabs
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,10 @@
|
||||||
import hashes, tables, intsets, std/sha1
|
import hashes, tables, intsets, std/sha1
|
||||||
import packed_ast, bitabs, rodfiles
|
import packed_ast, bitabs, rodfiles
|
||||||
import ".." / [ast, idents, lineinfos, msgs, ropes, options,
|
import ".." / [ast, idents, lineinfos, msgs, ropes, options,
|
||||||
pathutils, condsyms, packages, modulepaths]
|
pathutils, condsyms]
|
||||||
#import ".." / [renderer, astalgo]
|
#import ".." / [renderer, astalgo]
|
||||||
from os import removeFile, isAbsolute
|
from os import removeFile, isAbsolute
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[syncio, assertions, formatfloat]
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PackedConfig* = object
|
PackedConfig* = object
|
||||||
backend: TBackend
|
backend: TBackend
|
||||||
|
|
@ -43,10 +40,11 @@ type
|
||||||
reexports*: seq[(LitId, PackedItemId)]
|
reexports*: seq[(LitId, PackedItemId)]
|
||||||
compilerProcs*: seq[(LitId, int32)]
|
compilerProcs*: seq[(LitId, int32)]
|
||||||
converters*, methods*, trmacros*, pureEnums*: seq[int32]
|
converters*, methods*, trmacros*, pureEnums*: seq[int32]
|
||||||
|
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
|
||||||
|
|
||||||
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
||||||
procInstCache*: seq[PackedInstantiation]
|
procInstCache*: seq[PackedInstantiation]
|
||||||
attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
|
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
|
||||||
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
||||||
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
||||||
|
|
||||||
|
|
@ -96,10 +94,10 @@ proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int;
|
||||||
result.addInt m.numbers[LitId tree.nodes[pos].operand]
|
result.addInt m.numbers[LitId tree.nodes[pos].operand]
|
||||||
of externUIntLit:
|
of externUIntLit:
|
||||||
result.add " "
|
result.add " "
|
||||||
result.addInt cast[uint64](m.numbers[LitId tree.nodes[pos].operand])
|
result.add $cast[uint64](m.numbers[LitId tree.nodes[pos].operand])
|
||||||
of nkFloatLit..nkFloat128Lit:
|
of nkFloatLit..nkFloat128Lit:
|
||||||
result.add " "
|
result.add " "
|
||||||
result.addFloat cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand])
|
result.add $cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand])
|
||||||
else:
|
else:
|
||||||
result.add "(\n"
|
result.add "(\n"
|
||||||
for i in 1..(nesting+1)*2: result.add ' '
|
for i in 1..(nesting+1)*2: result.add ' '
|
||||||
|
|
@ -354,7 +352,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
|
||||||
|
|
||||||
var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
|
var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
|
||||||
size: t.size, align: t.align, nonUniqueId: t.itemId.item,
|
size: t.size, align: t.align, nonUniqueId: t.itemId.item,
|
||||||
paddingAtEnd: t.paddingAtEnd)
|
paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel)
|
||||||
storeNode(p, t, n)
|
storeNode(p, t, n)
|
||||||
p.typeInst = t.typeInst.storeType(c, m)
|
p.typeInst = t.typeInst.storeType(c, m)
|
||||||
for kid in items t.sons:
|
for kid in items t.sons:
|
||||||
|
|
@ -402,7 +400,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
|
||||||
p.bitsize = s.bitsize
|
p.bitsize = s.bitsize
|
||||||
p.alignment = s.alignment
|
p.alignment = s.alignment
|
||||||
|
|
||||||
p.externalName = toLitId(s.loc.r, m)
|
p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, m)
|
||||||
p.locFlags = s.loc.flags
|
p.locFlags = s.loc.flags
|
||||||
c.addMissing s.typ
|
c.addMissing s.typ
|
||||||
p.typ = s.typ.storeType(c, m)
|
p.typ = s.typ.storeType(c, m)
|
||||||
|
|
@ -509,9 +507,6 @@ proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var Pac
|
||||||
of nkStmtList, nkStmtListExpr:
|
of nkStmtList, nkStmtListExpr:
|
||||||
for it in n:
|
for it in n:
|
||||||
toPackedNodeIgnoreProcDefs(it, encoder, m)
|
toPackedNodeIgnoreProcDefs(it, encoder, m)
|
||||||
of nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
|
||||||
nkFromStmt, nkIncludeStmt:
|
|
||||||
discard "nothing to do"
|
|
||||||
else:
|
else:
|
||||||
toPackedNode(n, m.topLevel, encoder, m)
|
toPackedNode(n, m.topLevel, encoder, m)
|
||||||
|
|
||||||
|
|
@ -528,15 +523,6 @@ proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var Packe
|
||||||
toPackedProcDef(s.ast, m.topLevel, encoder, m)
|
toPackedProcDef(s.ast, m.topLevel, encoder, m)
|
||||||
#flush encoder, m
|
#flush encoder, m
|
||||||
|
|
||||||
proc storeAttachedProcDef*(t: PType; op: TTypeAttachedOp; s: PSym,
|
|
||||||
encoder: var PackedEncoder; m: var PackedModule) =
|
|
||||||
assert s.kind in routineKinds
|
|
||||||
assert isActive(encoder)
|
|
||||||
let tid = storeTypeLater(t, encoder, m)
|
|
||||||
let sid = storeSymLater(s, encoder, m)
|
|
||||||
m.attachedOps.add (tid, op, sid)
|
|
||||||
toPackedGeneratedProcDef(s, encoder, m)
|
|
||||||
|
|
||||||
proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: PInstantiation) =
|
proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: PInstantiation) =
|
||||||
var t = newSeq[PackedItemId](i.concreteTypes.len)
|
var t = newSeq[PackedItemId](i.concreteTypes.len)
|
||||||
for j in 0..high(i.concreteTypes):
|
for j in 0..high(i.concreteTypes):
|
||||||
|
|
@ -559,10 +545,6 @@ proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) =
|
||||||
rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err)
|
rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err)
|
||||||
#echo "Error: ", $err, " loading file: ", filename.string
|
#echo "Error: ", $err, " loading file: ", filename.string
|
||||||
|
|
||||||
proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
|
|
||||||
result = changeFileExt(completeGeneratedFilePath(conf,
|
|
||||||
mangleModuleName(conf, f).AbsoluteFile), ext)
|
|
||||||
|
|
||||||
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
|
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
|
||||||
ignoreConfig = false): RodFileError =
|
ignoreConfig = false): RodFileError =
|
||||||
var f = rodfiles.open(filename.string)
|
var f = rodfiles.open(filename.string)
|
||||||
|
|
@ -605,6 +587,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
|
||||||
loadSeqSection convertersSection, m.converters
|
loadSeqSection convertersSection, m.converters
|
||||||
loadSeqSection methodsSection, m.methods
|
loadSeqSection methodsSection, m.methods
|
||||||
loadSeqSection pureEnumsSection, m.pureEnums
|
loadSeqSection pureEnumsSection, m.pureEnums
|
||||||
|
loadSeqSection macroUsagesSection, m.macroUsages
|
||||||
|
|
||||||
loadSeqSection toReplaySection, m.toReplay.nodes
|
loadSeqSection toReplaySection, m.toReplay.nodes
|
||||||
loadSeqSection topLevelSection, m.topLevel.nodes
|
loadSeqSection topLevelSection, m.topLevel.nodes
|
||||||
|
|
@ -668,6 +651,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
|
||||||
storeSeqSection convertersSection, m.converters
|
storeSeqSection convertersSection, m.converters
|
||||||
storeSeqSection methodsSection, m.methods
|
storeSeqSection methodsSection, m.methods
|
||||||
storeSeqSection pureEnumsSection, m.pureEnums
|
storeSeqSection pureEnumsSection, m.pureEnums
|
||||||
|
storeSeqSection macroUsagesSection, m.macroUsages
|
||||||
|
|
||||||
storeSeqSection toReplaySection, m.toReplay.nodes
|
storeSeqSection toReplaySection, m.toReplay.nodes
|
||||||
storeSeqSection topLevelSection, m.topLevel.nodes
|
storeSeqSection topLevelSection, m.topLevel.nodes
|
||||||
|
|
@ -900,7 +884,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||||
t: PackedType; si, item: int32): PType =
|
t: PackedType; si, item: int32): PType =
|
||||||
result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind,
|
result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind,
|
||||||
flags: t.flags, size: t.size, align: t.align,
|
flags: t.flags, size: t.size, align: t.align,
|
||||||
paddingAtEnd: t.paddingAtEnd,
|
paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel,
|
||||||
uniqueId: ItemId(module: si, item: item),
|
uniqueId: ItemId(module: si, item: item),
|
||||||
callConv: t.callConv)
|
callConv: t.callConv)
|
||||||
|
|
||||||
|
|
@ -940,6 +924,17 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
|
||||||
result = g[si].types[t.item]
|
result = g[si].types[t.item]
|
||||||
assert result.itemId.item > 0
|
assert result.itemId.item > 0
|
||||||
|
|
||||||
|
proc newPackage(config: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
|
||||||
|
let filename = AbsoluteFile toFullPath(config, fileIdx)
|
||||||
|
let name = getIdent(cache, splitFile(filename).name)
|
||||||
|
let info = newLineInfo(fileIdx, 1, 1)
|
||||||
|
let
|
||||||
|
pck = getPackageName(config, filename.string)
|
||||||
|
pck2 = if pck.len > 0: pck else: "unknown"
|
||||||
|
pack = getIdent(cache, pck2)
|
||||||
|
result = newSym(skPackage, getIdent(cache, pck2),
|
||||||
|
ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info)
|
||||||
|
|
||||||
proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||||
fileIdx: FileIndex; m: var LoadedModule) =
|
fileIdx: FileIndex; m: var LoadedModule) =
|
||||||
m.iface = initTable[PIdent, seq[PackedItemId]]()
|
m.iface = initTable[PIdent, seq[PackedItemId]]()
|
||||||
|
|
@ -967,7 +962,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
|
||||||
name: getIdent(cache, splitFile(filename).name),
|
name: getIdent(cache, splitFile(filename).name),
|
||||||
info: newLineInfo(fileIdx, 1, 1),
|
info: newLineInfo(fileIdx, 1, 1),
|
||||||
position: int(fileIdx))
|
position: int(fileIdx))
|
||||||
m.module.owner = getPackage(conf, cache, fileIdx)
|
m.module.owner = newPackage(conf, cache, fileIdx)
|
||||||
m.module.flags = m.fromDisk.moduleFlags
|
m.module.flags = m.fromDisk.moduleFlags
|
||||||
|
|
||||||
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@
|
||||||
## The set must cover a complete Nim project.
|
## The set must cover a complete Nim project.
|
||||||
|
|
||||||
import sets
|
import sets
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".." / [ast, modulegraphs]
|
import ".." / [ast, modulegraphs]
|
||||||
import packed_ast, bitabs, ic
|
import packed_ast, bitabs, ic
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,6 @@ import sets
|
||||||
from os import nil
|
from os import nil
|
||||||
from std/private/miscdollars import toLocation
|
from std/private/miscdollars import toLocation
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".." / [ast, modulegraphs, msgs, options]
|
import ".." / [ast, modulegraphs, msgs, options]
|
||||||
import packed_ast, bitabs, ic
|
import packed_ast, bitabs, ic
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,6 @@ import hashes, tables, strtabs
|
||||||
import bitabs
|
import bitabs
|
||||||
import ".." / [ast, options]
|
import ".." / [ast, options]
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
SymId* = distinct int32
|
SymId* = distinct int32
|
||||||
ModuleId* = distinct int32
|
ModuleId* = distinct int32
|
||||||
|
|
@ -84,6 +81,7 @@ type
|
||||||
size*: BiggestInt
|
size*: BiggestInt
|
||||||
align*: int16
|
align*: int16
|
||||||
paddingAtEnd*: int16
|
paddingAtEnd*: int16
|
||||||
|
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||||
# not serialized: loc*: TLoc because it is backend-specific
|
# not serialized: loc*: TLoc because it is backend-specific
|
||||||
typeInst*: PackedItemId
|
typeInst*: PackedItemId
|
||||||
nonUniqueId*: int32
|
nonUniqueId*: int32
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,6 @@ import ".." / [ast, modulegraphs, trees, extccomp, btrees,
|
||||||
|
|
||||||
import tables
|
import tables
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import packed_ast, ic, bitabs
|
import packed_ast, ic, bitabs
|
||||||
|
|
||||||
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
||||||
|
|
@ -89,20 +86,6 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
||||||
else:
|
else:
|
||||||
internalAssert g.config, false
|
internalAssert g.config, false
|
||||||
|
|
||||||
proc replayBackendProcs*(g: ModuleGraph; module: int) =
|
|
||||||
for it in mitems(g.packed[module].fromDisk.attachedOps):
|
|
||||||
let key = translateId(it[0], g.packed, module, g.config)
|
|
||||||
let op = it[1]
|
|
||||||
let tmp = translateId(it[2], g.packed, module, g.config)
|
|
||||||
let symId = FullId(module: tmp.module, packed: it[2])
|
|
||||||
g.attachedOps[op][key] = LazySym(id: symId, sym: nil)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
|
|
||||||
let key = translateId(it[0], g.packed, module, g.config)
|
|
||||||
let tmp = translateId(it[1], g.packed, module, g.config)
|
|
||||||
let symId = FullId(module: tmp.module, packed: it[1])
|
|
||||||
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
|
|
||||||
|
|
||||||
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
||||||
## We remember the generic instantiations a module performed
|
## We remember the generic instantiations a module performed
|
||||||
## in order to to avoid the code bloat that generic code tends
|
## in order to to avoid the code bloat that generic code tends
|
||||||
|
|
@ -134,7 +117,11 @@ proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
||||||
let symId = FullId(module: tmp.module, packed: it[2])
|
let symId = FullId(module: tmp.module, packed: it[2])
|
||||||
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
|
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
|
||||||
|
|
||||||
replayBackendProcs(g, module)
|
for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
|
||||||
|
let key = translateId(it[0], g.packed, module, g.config)
|
||||||
|
let tmp = translateId(it[1], g.packed, module, g.config)
|
||||||
|
let symId = FullId(module: tmp.module, packed: it[1])
|
||||||
|
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.methods):
|
for it in mitems(g.packed[module].fromDisk.methods):
|
||||||
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
|
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,12 @@
|
||||||
|
|
||||||
from typetraits import supportsCopyMem
|
from typetraits import supportsCopyMem
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[syncio, assertions]
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
## ========
|
## ========
|
||||||
## `RodFile` represents a Rod File (versioned binary format), and the
|
## `RodFile` represents a Rod File (versioned binary format), and the
|
||||||
## associated data for common interactions such as IO and error tracking
|
## associated data for common interactions such as IO and error tracking
|
||||||
## (`RodFileError`). The file format broken up into sections (`RodSection`)
|
## (`RodFileError`). The file format broken up into sections (`RodSection`)
|
||||||
## and preceded by a header (see: `cookie`). The precise layout, section
|
## and preceeded by a header (see: `cookie`). The precise layout, section
|
||||||
## ordering and data following the section are determined by the user. See
|
## ordering and data following the section are determined by the user. See
|
||||||
## `ic.loadRodFile`.
|
## `ic.loadRodFile`.
|
||||||
##
|
##
|
||||||
|
|
@ -37,7 +34,8 @@ when defined(nimPreviewSlimSystem):
|
||||||
##
|
##
|
||||||
## Now read the bits below to understand what's missing.
|
## Now read the bits below to understand what's missing.
|
||||||
##
|
##
|
||||||
## ### Issues with the Example
|
## Issues with the Example
|
||||||
|
## ```````````````````````
|
||||||
## Missing Sections:
|
## Missing Sections:
|
||||||
## This is a low level API, so headers and sections need to be stored and
|
## This is a low level API, so headers and sections need to be stored and
|
||||||
## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
|
## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
|
||||||
|
|
@ -84,6 +82,7 @@ type
|
||||||
convertersSection
|
convertersSection
|
||||||
methodsSection
|
methodsSection
|
||||||
pureEnumsSection
|
pureEnumsSection
|
||||||
|
macroUsagesSection
|
||||||
toReplaySection
|
toReplaySection
|
||||||
topLevelSection
|
topLevelSection
|
||||||
bodiesSection
|
bodiesSection
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@
|
||||||
import
|
import
|
||||||
hashes, wordrecg
|
hashes, wordrecg
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PIdent* = ref TIdent
|
PIdent* = ref TIdent
|
||||||
TIdent*{.acyclic.} = object
|
TIdent*{.acyclic.} = object
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,7 @@
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, options, idents, lookups,
|
intsets, ast, astalgo, msgs, options, idents, lookups,
|
||||||
semdata, modulepaths, sigmatch, lineinfos, sets,
|
semdata, modulepaths, sigmatch, lineinfos, sets,
|
||||||
modulegraphs, wordrecg, tables
|
modulegraphs, wordrecg
|
||||||
from strutils import `%`
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc readExceptSet*(c: PContext, n: PNode): IntSet =
|
proc readExceptSet*(c: PContext, n: PNode): IntSet =
|
||||||
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
|
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
|
||||||
|
|
@ -188,13 +184,11 @@ template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
|
||||||
for it in mitems c.graph.ifaces[fromMod.position].converters:
|
for it in mitems c.graph.ifaces[fromMod.position].converters:
|
||||||
if filter:
|
if filter:
|
||||||
loadPackedSym(c.graph, it)
|
loadPackedSym(c.graph, it)
|
||||||
if sfExported in it.sym.flags:
|
addConverter(c, it)
|
||||||
addConverter(c, it)
|
|
||||||
for it in mitems c.graph.ifaces[fromMod.position].patterns:
|
for it in mitems c.graph.ifaces[fromMod.position].patterns:
|
||||||
if filter:
|
if filter:
|
||||||
loadPackedSym(c.graph, it)
|
loadPackedSym(c.graph, it)
|
||||||
if sfExported in it.sym.flags:
|
addPattern(c, it)
|
||||||
addPattern(c, it)
|
|
||||||
for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
|
for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
|
||||||
if filter:
|
if filter:
|
||||||
loadPackedSym(c.graph, it)
|
loadPackedSym(c.graph, it)
|
||||||
|
|
@ -202,7 +196,7 @@ template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
|
||||||
|
|
||||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||||
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
|
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
|
||||||
addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
|
addUnnamedIt(c, fromMod, it.sym.id notin exceptSet)
|
||||||
|
|
||||||
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
||||||
c.addImport ImportedModule(m: fromMod, mode: importAll)
|
c.addImport ImportedModule(m: fromMod, mode: importAll)
|
||||||
|
|
@ -230,21 +224,19 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||||
|
|
||||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||||
result = realModule
|
result = realModule
|
||||||
|
c.unusedImports.add((realModule, n.info))
|
||||||
template createModuleAliasImpl(ident): untyped =
|
template createModuleAliasImpl(ident): untyped =
|
||||||
createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
|
createModuleAlias(realModule, nextSymId c.idgen, ident, realModule.info, c.config.options)
|
||||||
if n.kind != nkImportAs: discard
|
if n.kind != nkImportAs: discard
|
||||||
elif n.len != 2 or n[1].kind != nkIdent:
|
elif n.len != 2 or n[1].kind != nkIdent:
|
||||||
localError(c.config, n.info, "module alias must be an identifier")
|
localError(c.config, n.info, "module alias must be an identifier")
|
||||||
elif n[1].ident.id != realModule.name.id:
|
elif n[1].ident.id != realModule.name.id:
|
||||||
# some misguided guy will write 'import abc.foo as foo' ...
|
# some misguided guy will write 'import abc.foo as foo' ...
|
||||||
result = createModuleAliasImpl(n[1].ident)
|
result = createModuleAliasImpl(n[1].ident)
|
||||||
if result == realModule:
|
|
||||||
# avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
|
|
||||||
result = createModuleAliasImpl(realModule.name)
|
|
||||||
if importHidden:
|
if importHidden:
|
||||||
|
if result == realModule: # avoids modifying `realModule`, see D20201209T194412.
|
||||||
|
result = createModuleAliasImpl(realModule.name)
|
||||||
result.options.incl optImportHidden
|
result.options.incl optImportHidden
|
||||||
c.unusedImports.add((result, n.info))
|
|
||||||
c.importModuleMap[result.id] = realModule.id
|
|
||||||
|
|
||||||
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
|
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
|
||||||
var ret: typeof(result)
|
var ret: typeof(result)
|
||||||
|
|
@ -282,33 +274,27 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||||
toFullPath(c.config, c.graph.importStack[i+1])
|
toFullPath(c.config, c.graph.importStack[i+1])
|
||||||
c.recursiveDep = err
|
c.recursiveDep = err
|
||||||
|
|
||||||
var realModule: PSym
|
|
||||||
discard pushOptionEntry(c)
|
discard pushOptionEntry(c)
|
||||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f), transf.importHidden)
|
||||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
|
||||||
popOptionEntry(c)
|
popOptionEntry(c)
|
||||||
|
|
||||||
#echo "set back to ", L
|
#echo "set back to ", L
|
||||||
c.graph.importStack.setLen(L)
|
c.graph.importStack.setLen(L)
|
||||||
# we cannot perform this check reliably because of
|
# we cannot perform this check reliably because of
|
||||||
# test: modules/import_in_config) # xxx is that still true?
|
# test: modules/import_in_config)
|
||||||
if realModule == c.module:
|
when true:
|
||||||
localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s)
|
if result.info.fileIndex == c.module.info.fileIndex and
|
||||||
if sfDeprecated in realModule.flags:
|
result.info.fileIndex == n.info.fileIndex:
|
||||||
var prefix = ""
|
localError(c.config, n.info, "A module cannot import itself")
|
||||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
if sfDeprecated in result.flags:
|
||||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
if result.constraint != nil:
|
||||||
|
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
|
||||||
|
else:
|
||||||
|
message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
|
||||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
||||||
importStmtResult.add newSymNode(result, n.info)
|
importStmtResult.add newSymNode(result, n.info)
|
||||||
#newStrNode(toFullPath(c.config, f), n.info)
|
#newStrNode(toFullPath(c.config, f), n.info)
|
||||||
|
|
||||||
proc afterImport(c: PContext, m: PSym) =
|
|
||||||
# fixes bug #17510, for re-exported symbols
|
|
||||||
let realModuleId = c.importModuleMap[m.id]
|
|
||||||
for s in allSyms(c.graph, m):
|
|
||||||
if s.owner.id != realModuleId:
|
|
||||||
c.exportIndirections.incl((m.id, s.id))
|
|
||||||
|
|
||||||
proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||||
var it = it
|
var it = it
|
||||||
let m = myImportModule(c, it, importStmtResult)
|
let m = myImportModule(c, it, importStmtResult)
|
||||||
|
|
@ -317,7 +303,6 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
addDecl(c, m, it.info) # add symbol to symbol table of module
|
||||||
importAllSymbols(c, m)
|
importAllSymbols(c, m)
|
||||||
#importForwarded(c, m.ast, emptySet, m)
|
#importForwarded(c, m.ast, emptySet, m)
|
||||||
afterImport(c, m)
|
|
||||||
|
|
||||||
proc evalImport*(c: PContext, n: PNode): PNode =
|
proc evalImport*(c: PContext, n: PNode): PNode =
|
||||||
result = newNodeI(nkImportStmt, n.info)
|
result = newNodeI(nkImportStmt, n.info)
|
||||||
|
|
@ -356,7 +341,6 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
|
||||||
if n[i].kind != nkNilLit:
|
if n[i].kind != nkNilLit:
|
||||||
importSymbol(c, n[i], m, im.imported)
|
importSymbol(c, n[i], m, im.imported)
|
||||||
c.addImport im
|
c.addImport im
|
||||||
afterImport(c, m)
|
|
||||||
|
|
||||||
proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||||
result = newNodeI(nkImportStmt, n.info)
|
result = newNodeI(nkImportStmt, n.info)
|
||||||
|
|
@ -367,4 +351,3 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||||
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
||||||
#importForwarded(c, m.ast, exceptSet, m)
|
#importForwarded(c, m.ast, exceptSet, m)
|
||||||
afterImport(c, m)
|
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,21 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
|
intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
|
||||||
strutils, options, lowerings, tables, modulegraphs,
|
strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
|
||||||
lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
|
lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
|
||||||
varpartitions, aliasanalysis, dfa
|
varpartitions
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
from trees import exprStructuralEquivalent, getRoot
|
from trees import exprStructuralEquivalent, getRoot
|
||||||
|
|
||||||
type
|
type
|
||||||
Con = object
|
Con = object
|
||||||
owner: PSym
|
owner: PSym
|
||||||
when true:
|
g: ControlFlowGraph
|
||||||
g: ControlFlowGraph
|
|
||||||
graph: ModuleGraph
|
graph: ModuleGraph
|
||||||
inLoop, inSpawn, inLoopCond: int
|
inLoop, inSpawn, inLoopCond: int
|
||||||
uninit: IntSet # set of uninit'ed vars
|
uninit: IntSet # set of uninit'ed vars
|
||||||
|
uninitComputed: bool
|
||||||
idgen: IdGenerator
|
idgen: IdGenerator
|
||||||
body: PNode
|
|
||||||
otherUsage: TLineInfo
|
|
||||||
|
|
||||||
Scope = object # we do scope-based memory management.
|
Scope = object # we do scope-based memory management.
|
||||||
# a scope is comparable to an nkStmtListExpr like
|
# a scope is comparable to an nkStmtListExpr like
|
||||||
|
|
@ -42,8 +37,6 @@ type
|
||||||
vars: seq[PSym]
|
vars: seq[PSym]
|
||||||
wasMoved: seq[PNode]
|
wasMoved: seq[PNode]
|
||||||
final: seq[PNode] # finally section
|
final: seq[PNode] # finally section
|
||||||
locals: seq[PSym]
|
|
||||||
body: PNode
|
|
||||||
needsTry: bool
|
needsTry: bool
|
||||||
parent: ptr Scope
|
parent: ptr Scope
|
||||||
|
|
||||||
|
|
@ -74,100 +67,167 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
||||||
s.vars.add(sym)
|
s.vars.add(sym)
|
||||||
result = newSymNode(sym)
|
result = newSymNode(sym)
|
||||||
|
|
||||||
proc nestedScope(parent: var Scope; body: PNode): Scope =
|
proc nestedScope(parent: var Scope): Scope =
|
||||||
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
|
Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent))
|
||||||
|
|
||||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
|
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode
|
||||||
|
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode
|
||||||
|
|
||||||
|
import sets, hashes, tables
|
||||||
|
|
||||||
|
proc hash(n: PNode): Hash = hash(cast[pointer](n))
|
||||||
|
|
||||||
|
proc aliasesCached(cache: var Table[(PNode, PNode), AliasKind], obj, field: PNode): AliasKind =
|
||||||
|
let key = (obj, field)
|
||||||
|
if not cache.hasKey(key):
|
||||||
|
cache[key] = aliases(obj, field)
|
||||||
|
cache[key]
|
||||||
|
|
||||||
type
|
type
|
||||||
MoveOrCopyFlag = enum
|
State = ref object
|
||||||
IsDecl, IsExplicitSink
|
lastReads: IntSet
|
||||||
|
potentialLastReads: IntSet
|
||||||
|
notLastReads: IntSet
|
||||||
|
alreadySeen: HashSet[PNode]
|
||||||
|
|
||||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
|
proc preprocessCfg(cfg: var ControlFlowGraph) =
|
||||||
|
for i in 0..<cfg.len:
|
||||||
|
if cfg[i].kind in {goto, fork} and i + cfg[i].dest > cfg.len:
|
||||||
|
cfg[i].dest = cfg.len - i
|
||||||
|
|
||||||
when false:
|
proc mergeStates(a: var State, b: sink State) =
|
||||||
var
|
# Inplace for performance:
|
||||||
perfCounters: array[InstrKind, int]
|
# lastReads = a.lastReads + b.lastReads
|
||||||
|
# potentialLastReads = (a.potentialLastReads + b.potentialLastReads) - (a.notLastReads + b.notLastReads)
|
||||||
proc showCounters*() =
|
# notLastReads = a.notLastReads + b.notLastReads
|
||||||
for i in low(InstrKind)..high(InstrKind):
|
# alreadySeen = a.alreadySeen + b.alreadySeen
|
||||||
echo "INSTR ", i, " ", perfCounters[i]
|
# b is never nil
|
||||||
|
if a == nil:
|
||||||
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
a = b
|
||||||
let root = parampatterns.exprRoot(n, allowCalls=false)
|
|
||||||
if root == nil: return false
|
|
||||||
|
|
||||||
var s = addr(scope)
|
|
||||||
while s != nil:
|
|
||||||
if s.locals.contains(root): break
|
|
||||||
s = s.parent
|
|
||||||
|
|
||||||
c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root)
|
|
||||||
dbg:
|
|
||||||
echo "\n### ", c.owner.name.s, ":\nCFG:"
|
|
||||||
echoCfg(c.g)
|
|
||||||
#echo c.body
|
|
||||||
|
|
||||||
var j = 0
|
|
||||||
while j < c.g.len:
|
|
||||||
if c.g[j].kind == use and c.g[j].n == n: break
|
|
||||||
inc j
|
|
||||||
c.otherUsage = unknownLineInfo
|
|
||||||
if j < c.g.len:
|
|
||||||
var pcs = @[j+1]
|
|
||||||
var marked = initIntSet()
|
|
||||||
result = true
|
|
||||||
while pcs.len > 0:
|
|
||||||
var pc = pcs.pop()
|
|
||||||
if not marked.contains(pc):
|
|
||||||
let oldPc = pc
|
|
||||||
while pc < c.g.len:
|
|
||||||
dbg:
|
|
||||||
echo "EXEC ", c.g[pc].kind, " ", pc, " ", n
|
|
||||||
when false:
|
|
||||||
inc perfCounters[c.g[pc].kind]
|
|
||||||
case c.g[pc].kind
|
|
||||||
of loop:
|
|
||||||
let back = pc + c.g[pc].dest
|
|
||||||
if not marked.containsOrIncl(back):
|
|
||||||
pc = back
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
of goto:
|
|
||||||
pc = pc + c.g[pc].dest
|
|
||||||
of fork:
|
|
||||||
if not marked.contains(pc+1):
|
|
||||||
pcs.add pc + 1
|
|
||||||
pc = pc + c.g[pc].dest
|
|
||||||
of use:
|
|
||||||
if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no:
|
|
||||||
c.otherUsage = c.g[pc].n.info
|
|
||||||
return false
|
|
||||||
inc pc
|
|
||||||
of def:
|
|
||||||
if c.g[pc].n.aliases(n) == yes:
|
|
||||||
# the path leads to a redefinition of 's' --> sink 's'.
|
|
||||||
break
|
|
||||||
elif n.aliases(c.g[pc].n) != no:
|
|
||||||
# only partially writes to 's' --> can't sink 's', so this def reads 's'
|
|
||||||
# or maybe writes to 's' --> can't sink 's'
|
|
||||||
c.otherUsage = c.g[pc].n.info
|
|
||||||
return false
|
|
||||||
inc pc
|
|
||||||
marked.incl oldPc
|
|
||||||
else:
|
else:
|
||||||
result = false
|
a.lastReads.incl b.lastReads
|
||||||
|
a.potentialLastReads.incl b.potentialLastReads
|
||||||
|
a.potentialLastReads.excl a.notLastReads
|
||||||
|
a.potentialLastReads.excl b.notLastReads
|
||||||
|
a.notLastReads.incl b.notLastReads
|
||||||
|
a.alreadySeen.incl b.alreadySeen
|
||||||
|
|
||||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) =
|
||||||
if not hasDestructor(c, n.typ): return true
|
var cache = initTable[(PNode, PNode), AliasKind]()
|
||||||
|
template aliasesCached(obj, field: PNode): AliasKind =
|
||||||
|
aliasesCached(cache, obj, field)
|
||||||
|
|
||||||
let m = skipConvDfa(n)
|
var cfg = cfg
|
||||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
preprocessCfg(cfg)
|
||||||
isLastReadImpl(n, c, s)
|
|
||||||
|
var states = newSeq[State](cfg.len + 1)
|
||||||
|
states[0] = State()
|
||||||
|
|
||||||
|
for pc in 0..<cfg.len:
|
||||||
|
template state: State = states[pc]
|
||||||
|
if state != nil:
|
||||||
|
case cfg[pc].kind
|
||||||
|
of def:
|
||||||
|
var potentialLastReadsCopy = state.potentialLastReads
|
||||||
|
for r in potentialLastReadsCopy:
|
||||||
|
if cfg[pc].n.aliasesCached(cfg[r].n) == yes:
|
||||||
|
# the path leads to a redefinition of 's' --> sink 's'.
|
||||||
|
state.lastReads.incl r
|
||||||
|
state.potentialLastReads.excl r
|
||||||
|
elif cfg[r].n.aliasesCached(cfg[pc].n) != no:
|
||||||
|
# only partially writes to 's' --> can't sink 's', so this def reads 's'
|
||||||
|
# or maybe writes to 's' --> can't sink 's'
|
||||||
|
cfg[r].n.comment = '\n' & $pc
|
||||||
|
state.potentialLastReads.excl r
|
||||||
|
state.notLastReads.incl r
|
||||||
|
|
||||||
|
var alreadySeenThisNode = false
|
||||||
|
for s in state.alreadySeen:
|
||||||
|
if cfg[pc].n.aliasesCached(s) != no or s.aliasesCached(cfg[pc].n) != no:
|
||||||
|
alreadySeenThisNode = true; break
|
||||||
|
if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite
|
||||||
|
else: cfg[pc].n.flags.incl nfFirstWrite
|
||||||
|
|
||||||
|
state.alreadySeen.incl cfg[pc].n
|
||||||
|
|
||||||
|
mergeStates(states[pc + 1], move(states[pc]))
|
||||||
|
of use:
|
||||||
|
var potentialLastReadsCopy = state.potentialLastReads
|
||||||
|
for r in potentialLastReadsCopy:
|
||||||
|
if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no:
|
||||||
|
cfg[r].n.comment = '\n' & $pc
|
||||||
|
state.potentialLastReads.excl r
|
||||||
|
state.notLastReads.incl r
|
||||||
|
|
||||||
|
state.potentialLastReads.incl pc
|
||||||
|
|
||||||
|
state.alreadySeen.incl cfg[pc].n
|
||||||
|
|
||||||
|
mergeStates(states[pc + 1], move(states[pc]))
|
||||||
|
of goto:
|
||||||
|
mergeStates(states[pc + cfg[pc].dest], move(states[pc]))
|
||||||
|
of fork:
|
||||||
|
var copy = State()
|
||||||
|
copy[] = states[pc][]
|
||||||
|
mergeStates(states[pc + cfg[pc].dest], copy)
|
||||||
|
mergeStates(states[pc + 1], move(states[pc]))
|
||||||
|
|
||||||
|
let lastReads = (states[^1].lastReads + states[^1].potentialLastReads) - states[^1].notLastReads
|
||||||
|
var lastReadTable: Table[PNode, seq[int]]
|
||||||
|
for position, node in cfg:
|
||||||
|
if node.kind == use:
|
||||||
|
lastReadTable.mgetOrPut(node.n, @[]).add position
|
||||||
|
for node, positions in lastReadTable:
|
||||||
|
block checkIfAllPosLastRead:
|
||||||
|
for p in positions:
|
||||||
|
if p notin lastReads: break checkIfAllPosLastRead
|
||||||
|
node.flags.incl nfLastRead
|
||||||
|
|
||||||
|
proc isLastRead(n: PNode; c: var Con): bool =
|
||||||
|
let m = dfa.skipConvDfa(n)
|
||||||
|
(m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or nfLastRead in m.flags
|
||||||
|
|
||||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||||
let m = skipConvDfa(n)
|
let m = dfa.skipConvDfa(n)
|
||||||
result = nfFirstWrite in m.flags
|
nfFirstWrite in m.flags
|
||||||
|
|
||||||
|
proc initialized(code: ControlFlowGraph; pc: int,
|
||||||
|
init, uninit: var IntSet; until: int): int =
|
||||||
|
## Computes the set of definitely initialized variables across all code paths
|
||||||
|
## as an IntSet of IDs.
|
||||||
|
var pc = pc
|
||||||
|
while pc < code.len:
|
||||||
|
case code[pc].kind
|
||||||
|
of goto:
|
||||||
|
pc += code[pc].dest
|
||||||
|
of fork:
|
||||||
|
var initA = initIntSet()
|
||||||
|
var initB = initIntSet()
|
||||||
|
var variantA = pc + 1
|
||||||
|
var variantB = pc + code[pc].dest
|
||||||
|
while variantA != variantB:
|
||||||
|
if max(variantA, variantB) > until:
|
||||||
|
break
|
||||||
|
if variantA < variantB:
|
||||||
|
variantA = initialized(code, variantA, initA, uninit, min(variantB, until))
|
||||||
|
else:
|
||||||
|
variantB = initialized(code, variantB, initB, uninit, min(variantA, until))
|
||||||
|
pc = min(variantA, variantB)
|
||||||
|
# we add vars if they are in both branches:
|
||||||
|
for v in initA:
|
||||||
|
if v in initB:
|
||||||
|
init.incl v
|
||||||
|
of use:
|
||||||
|
let v = code[pc].n.sym
|
||||||
|
if v.kind != skParam and v.id notin init:
|
||||||
|
# attempt to read an uninit'ed variable
|
||||||
|
uninit.incl v.id
|
||||||
|
inc pc
|
||||||
|
of def:
|
||||||
|
let v = code[pc].n.sym
|
||||||
|
init.incl v.id
|
||||||
|
inc pc
|
||||||
|
return pc
|
||||||
|
|
||||||
proc isCursor(n: PNode): bool =
|
proc isCursor(n: PNode): bool =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -185,17 +245,17 @@ template isUnpackedTuple(n: PNode): bool =
|
||||||
## hence unpacked tuples themselves don't need to be destroyed
|
## hence unpacked tuples themselves don't need to be destroyed
|
||||||
(n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
|
(n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
|
||||||
|
|
||||||
|
from strutils import parseInt
|
||||||
|
|
||||||
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
||||||
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
||||||
if (opname == "=" or opname == "=copy") and ri != nil:
|
if (opname == "=" or opname == "=copy") and ri != nil:
|
||||||
m.add "; requires a copy because it's not the last read of '"
|
m.add "; requires a copy because it's not the last read of '"
|
||||||
m.add renderTree(ri)
|
m.add renderTree(ri)
|
||||||
m.add '\''
|
m.add '\''
|
||||||
if c.otherUsage != unknownLineInfo:
|
if ri.comment.startsWith('\n'):
|
||||||
# ri.comment.startsWith('\n'):
|
|
||||||
m.add "; another read is done here: "
|
m.add "; another read is done here: "
|
||||||
m.add c.graph.config $ c.otherUsage
|
m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
|
||||||
#m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
|
|
||||||
elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
|
elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
|
||||||
m.add "; try to make "
|
m.add "; try to make "
|
||||||
m.add renderTree(ri)
|
m.add renderTree(ri)
|
||||||
|
|
@ -217,7 +277,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
|
||||||
var op = getAttachedOp(c.graph, t, kind)
|
var op = getAttachedOp(c.graph, t, kind)
|
||||||
if op == nil or op.ast.isGenericRoutine:
|
if op == nil or op.ast.isGenericRoutine:
|
||||||
# give up and find the canonical type instead:
|
# give up and find the canonical type instead:
|
||||||
let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct})
|
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
|
||||||
let canon = c.graph.canonTypes.getOrDefault(h)
|
let canon = c.graph.canonTypes.getOrDefault(h)
|
||||||
if canon != nil:
|
if canon != nil:
|
||||||
op = getAttachedOp(c.graph, canon, kind)
|
op = getAttachedOp(c.graph, canon, kind)
|
||||||
|
|
@ -248,8 +308,8 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
|
||||||
proc isNoInit(dest: PNode): bool {.inline.} =
|
proc isNoInit(dest: PNode): bool {.inline.} =
|
||||||
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
|
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
|
||||||
|
|
||||||
proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
|
||||||
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
|
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or isDecl or
|
||||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||||
isNoInit(dest):
|
isNoInit(dest):
|
||||||
# optimize sink call into a bitwise memcopy
|
# optimize sink call into a bitwise memcopy
|
||||||
|
|
@ -301,22 +361,16 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
|
||||||
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
||||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
||||||
|
|
||||||
proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
|
proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode =
|
||||||
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
result = c.genOp(t, a, dest, ri)
|
result = c.genOp(t, attachedAsgn, dest, ri)
|
||||||
assert ri.typ != nil
|
|
||||||
|
|
||||||
proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
|
proc genCopy(c: var Con; dest, ri: PNode): PNode =
|
||||||
let t = dest.typ
|
let t = dest.typ
|
||||||
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||||
# try to improve the error message here:
|
# try to improve the error message here:
|
||||||
if IsExplicitSink in flags:
|
c.checkForErrorPragma(t, ri, "=copy")
|
||||||
c.checkForErrorPragma(t, ri, "=sink")
|
result = c.genCopyNoCheck(dest, ri)
|
||||||
else:
|
|
||||||
c.checkForErrorPragma(t, ri, "=copy")
|
|
||||||
let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn
|
|
||||||
result = c.genCopyNoCheck(dest, ri, a)
|
|
||||||
assert ri.typ != nil
|
|
||||||
|
|
||||||
proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
||||||
# discriminator is ordinal value that doesn't need sink destroy
|
# discriminator is ordinal value that doesn't need sink destroy
|
||||||
|
|
@ -354,18 +408,11 @@ It is best to factor out piece of object that needs custom destructor into separ
|
||||||
result.add newTree(nkFastAsgn, le, tmp)
|
result.add newTree(nkFastAsgn, le, tmp)
|
||||||
|
|
||||||
proc genWasMoved(c: var Con, n: PNode): PNode =
|
proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||||
let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
result = newNodeI(nkCall, n.info)
|
||||||
let op = getAttachedOp(c.graph, n.typ, attachedWasMoved)
|
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
|
||||||
if op != nil:
|
result.add copyTree(n) #mWasMoved does not take the address
|
||||||
if sfError in op.flags:
|
#if n.kind != nkSym:
|
||||||
c.checkForErrorPragma(n.typ, n, "=wasMoved")
|
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
|
||||||
result = genOp(c, op, n)
|
|
||||||
else:
|
|
||||||
result = newNodeI(nkCall, n.info)
|
|
||||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
|
|
||||||
result.add copyTree(n) #mWasMoved does not take the address
|
|
||||||
#if n.kind != nkSym:
|
|
||||||
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
|
|
||||||
|
|
||||||
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||||
result = newNodeI(nkCall, info)
|
result = newNodeI(nkCall, info)
|
||||||
|
|
@ -407,7 +454,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||||
let tmp = c.getTemp(s, n.typ, n.info)
|
let tmp = c.getTemp(s, n.typ, n.info)
|
||||||
if hasDestructor(c, n.typ):
|
if hasDestructor(c, n.typ):
|
||||||
result.add c.genWasMoved(tmp)
|
result.add c.genWasMoved(tmp)
|
||||||
var m = c.genCopy(tmp, n, {})
|
var m = c.genCopy(tmp, n)
|
||||||
m.add p(n, c, s, normal)
|
m.add p(n, c, s, normal)
|
||||||
c.finishCopy(m, n, isFromSink = true)
|
c.finishCopy(m, n, isFromSink = true)
|
||||||
result.add m
|
result.add m
|
||||||
|
|
@ -452,7 +499,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
|
||||||
# This was already done in the sink parameter handling logic.
|
# This was already done in the sink parameter handling logic.
|
||||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||||
let tmp = c.getTemp(s, arg.typ, arg.info)
|
let tmp = c.getTemp(s, arg.typ, arg.info)
|
||||||
result.add c.genSink(tmp, arg, {IsDecl})
|
result.add c.genSink(tmp, arg, isDecl = true)
|
||||||
result.add tmp
|
result.add tmp
|
||||||
s.final.add c.genDestroy(tmp)
|
s.final.add c.genDestroy(tmp)
|
||||||
else:
|
else:
|
||||||
|
|
@ -497,7 +544,7 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
|
||||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||||
elif sfThread notin v.sym.flags and sfCursor notin v.sym.flags:
|
elif sfThread notin v.sym.flags and sfCursor notin v.sym.flags:
|
||||||
# do not destroy thread vars for now at all for consistency.
|
# do not destroy thread vars for now at all for consistency.
|
||||||
if {sfGlobal, sfPure} <= v.sym.flags or sfGlobal in v.sym.flags and s.parent == nil:
|
if sfGlobal in v.sym.flags and s.parent == nil: #XXX: Rethink this logic (see tarcmisc.test2)
|
||||||
c.graph.globalDestructors.add c.genDestroy(v)
|
c.graph.globalDestructors.add c.genDestroy(v)
|
||||||
else:
|
else:
|
||||||
s.final.add c.genDestroy(v)
|
s.final.add c.genDestroy(v)
|
||||||
|
|
@ -524,17 +571,17 @@ proc processScope(c: var Con; s: var Scope; ret: PNode): PNode =
|
||||||
|
|
||||||
if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
|
if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
|
||||||
|
|
||||||
template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped, tmpFlags: TSymFlags): PNode =
|
template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped): PNode =
|
||||||
assert not ret.typ.isEmptyType
|
assert not ret.typ.isEmptyType
|
||||||
var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ)
|
var result = newNodeI(nkStmtListExpr, ret.info)
|
||||||
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
|
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
|
||||||
# later and use it to eliminate the temporary when theres no need for it, but its
|
# later and use it to eliminate the temporary when theres no need for it, but its
|
||||||
# tricky because you would have to intercept moveOrCopy at a certain point
|
# tricky because you would have to intercept moveOrCopy at a certain point
|
||||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
||||||
tmp.sym.flags = tmpFlags
|
tmp.sym.flags.incl sfSingleUsedTemp
|
||||||
let cpy = if hasDestructor(c, ret.typ):
|
let cpy = if hasDestructor(c, ret.typ):
|
||||||
s.parent[].final.add c.genDestroy(tmp)
|
s.parent[].final.add c.genDestroy(tmp)
|
||||||
moveOrCopy(tmp, ret, c, s, {IsDecl})
|
moveOrCopy(tmp, ret, c, s, isDecl = true)
|
||||||
else:
|
else:
|
||||||
newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
|
newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
|
||||||
|
|
||||||
|
|
@ -558,8 +605,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
||||||
template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
|
||||||
tmpFlags = {sfSingleUsedTemp}) =
|
|
||||||
template maybeVoid(child, s): untyped =
|
template maybeVoid(child, s): untyped =
|
||||||
if isEmptyType(child.typ): p(child, c, s, normal)
|
if isEmptyType(child.typ): p(child, c, s, normal)
|
||||||
else: processCall(child, s)
|
else: processCall(child, s)
|
||||||
|
|
@ -583,11 +629,11 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||||
var branch = shallowCopy(it)
|
var branch = shallowCopy(it)
|
||||||
for j in 0 ..< it.len-1:
|
for j in 0 ..< it.len-1:
|
||||||
branch[j] = copyTree(it[j])
|
branch[j] = copyTree(it[j])
|
||||||
var ofScope = nestedScope(s, it.lastSon)
|
var ofScope = nestedScope(s)
|
||||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
||||||
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
|
processScope(c, ofScope, maybeVoid(it[^1], ofScope))
|
||||||
else:
|
else:
|
||||||
processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
|
processScopeExpr(c, ofScope, it[^1], processCall)
|
||||||
result.add branch
|
result.add branch
|
||||||
|
|
||||||
of nkWhileStmt:
|
of nkWhileStmt:
|
||||||
|
|
@ -596,7 +642,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
result.add p(n[0], c, s, normal)
|
result.add p(n[0], c, s, normal)
|
||||||
dec c.inLoopCond
|
dec c.inLoopCond
|
||||||
var bodyScope = nestedScope(s, n[1])
|
var bodyScope = nestedScope(s)
|
||||||
let bodyResult = p(n[1], c, bodyScope, normal)
|
let bodyResult = p(n[1], c, bodyScope, normal)
|
||||||
result.add processScope(c, bodyScope, bodyResult)
|
result.add processScope(c, bodyScope, bodyResult)
|
||||||
dec c.inLoop
|
dec c.inLoop
|
||||||
|
|
@ -608,7 +654,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||||
for i in 0..<last-1:
|
for i in 0..<last-1:
|
||||||
result[i] = n[i]
|
result[i] = n[i]
|
||||||
result[last-1] = p(n[last-1], c, s, normal)
|
result[last-1] = p(n[last-1], c, s, normal)
|
||||||
var bodyScope = nestedScope(s, n[1])
|
var bodyScope = nestedScope(s)
|
||||||
let bodyResult = p(n[last], c, bodyScope, normal)
|
let bodyResult = p(n[last], c, bodyScope, normal)
|
||||||
result[last] = processScope(c, bodyScope, bodyResult)
|
result[last] = processScope(c, bodyScope, bodyResult)
|
||||||
dec c.inLoop
|
dec c.inLoop
|
||||||
|
|
@ -616,18 +662,18 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||||
of nkBlockStmt, nkBlockExpr:
|
of nkBlockStmt, nkBlockExpr:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
result.add n[0]
|
result.add n[0]
|
||||||
var bodyScope = nestedScope(s, n[1])
|
var bodyScope = nestedScope(s)
|
||||||
result.add if n[1].typ.isEmptyType or willProduceStmt:
|
result.add if n[1].typ.isEmptyType or willProduceStmt:
|
||||||
processScope(c, bodyScope, processCall(n[1], bodyScope))
|
processScope(c, bodyScope, processCall(n[1], bodyScope))
|
||||||
else:
|
else:
|
||||||
processScopeExpr(c, bodyScope, n[1], processCall, tmpFlags)
|
processScopeExpr(c, bodyScope, n[1], processCall)
|
||||||
|
|
||||||
of nkIfStmt, nkIfExpr:
|
of nkIfStmt, nkIfExpr:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
let it = n[i]
|
let it = n[i]
|
||||||
var branch = shallowCopy(it)
|
var branch = shallowCopy(it)
|
||||||
var branchScope = nestedScope(s, it.lastSon)
|
var branchScope = nestedScope(s)
|
||||||
if it.kind in {nkElifBranch, nkElifExpr}:
|
if it.kind in {nkElifBranch, nkElifExpr}:
|
||||||
#Condition needs to be destroyed outside of the condition/branch scope
|
#Condition needs to be destroyed outside of the condition/branch scope
|
||||||
branch[0] = p(it[0], c, s, normal)
|
branch[0] = p(it[0], c, s, normal)
|
||||||
|
|
@ -635,26 +681,26 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
|
||||||
processScope(c, branchScope, maybeVoid(it[^1], branchScope))
|
processScope(c, branchScope, maybeVoid(it[^1], branchScope))
|
||||||
else:
|
else:
|
||||||
processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
|
processScopeExpr(c, branchScope, it[^1], processCall)
|
||||||
result.add branch
|
result.add branch
|
||||||
|
|
||||||
of nkTryStmt:
|
of nkTryStmt:
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
var tryScope = nestedScope(s, n[0])
|
var tryScope = nestedScope(s)
|
||||||
result.add if n[0].typ.isEmptyType or willProduceStmt:
|
result.add if n[0].typ.isEmptyType or willProduceStmt:
|
||||||
processScope(c, tryScope, maybeVoid(n[0], tryScope))
|
processScope(c, tryScope, maybeVoid(n[0], tryScope))
|
||||||
else:
|
else:
|
||||||
processScopeExpr(c, tryScope, n[0], maybeVoid, tmpFlags)
|
processScopeExpr(c, tryScope, n[0], maybeVoid)
|
||||||
|
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
let it = n[i]
|
let it = n[i]
|
||||||
var branch = copyTree(it)
|
var branch = copyTree(it)
|
||||||
var branchScope = nestedScope(s, it[^1])
|
var branchScope = nestedScope(s)
|
||||||
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
|
branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
|
||||||
processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
|
processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
|
||||||
else: maybeVoid(it[^1], branchScope))
|
else: maybeVoid(it[^1], branchScope))
|
||||||
else:
|
else:
|
||||||
processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
|
processScopeExpr(c, branchScope, it[^1], processCall)
|
||||||
result.add branch
|
result.add branch
|
||||||
|
|
||||||
of nkWhen: # This should be a "when nimvm" node.
|
of nkWhen: # This should be a "when nimvm" node.
|
||||||
|
|
@ -670,7 +716,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||||
result.add call
|
result.add call
|
||||||
else:
|
else:
|
||||||
let tmp = c.getTemp(s, n[0].typ, n.info)
|
let tmp = c.getTemp(s, n[0].typ, n.info)
|
||||||
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
|
var m = c.genCopyNoCheck(tmp, n[0])
|
||||||
m.add p(n[0], c, s, normal)
|
m.add p(n[0], c, s, normal)
|
||||||
c.finishCopy(m, n[0], isFromSink = false)
|
c.finishCopy(m, n[0], isFromSink = false)
|
||||||
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
||||||
|
|
@ -687,11 +733,11 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||||
result.add copyNode(n[0])
|
result.add copyNode(n[0])
|
||||||
s.needsTry = true
|
s.needsTry = true
|
||||||
|
|
||||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode =
|
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
||||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt}:
|
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt}:
|
||||||
template process(child, s): untyped = p(child, c, s, mode)
|
template process(child, s): untyped = p(child, c, s, mode)
|
||||||
handleNestedTempl(n, process, tmpFlags = tmpFlags)
|
handleNestedTempl(n, process)
|
||||||
elif mode == sinkArg:
|
elif mode == sinkArg:
|
||||||
if n.containsConstSeq:
|
if n.containsConstSeq:
|
||||||
# const sequences are not mutable and so we need to pass a copy to the
|
# const sequences are not mutable and so we need to pass a copy to the
|
||||||
|
|
@ -702,7 +748,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||||
nkCallKinds + nkLiterals:
|
nkCallKinds + nkLiterals:
|
||||||
result = p(n, c, s, consumed)
|
result = p(n, c, s, consumed)
|
||||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
isLastRead(n, c) and not (n.kind == nkSym and isCursor(n)):
|
||||||
# Sinked params can be consumed only once. We need to reset the memory
|
# Sinked params can be consumed only once. We need to reset the memory
|
||||||
# to disable the destructor which we have not elided
|
# to disable the destructor which we have not elided
|
||||||
result = destructiveMoveVar(n, c, s)
|
result = destructiveMoveVar(n, c, s)
|
||||||
|
|
@ -728,7 +774,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||||
result = passCopyToSink(n, c, s)
|
result = passCopyToSink(n, c, s)
|
||||||
else:
|
else:
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkBracket, nkTupleConstr, nkClosure, nkCurly:
|
of nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkCurly:
|
||||||
# Let C(x) be the construction, 'x' the vector of arguments.
|
# Let C(x) be the construction, 'x' the vector of arguments.
|
||||||
# C(x) either owns 'x' or it doesn't.
|
# C(x) either owns 'x' or it doesn't.
|
||||||
# If C(x) owns its data, we must consume C(x).
|
# If C(x) owns its data, we must consume C(x).
|
||||||
|
|
@ -739,34 +785,18 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||||
# don't destroy it"
|
# don't destroy it"
|
||||||
# but if C(x) is a ref it MUST own its data since we must destroy it
|
# but if C(x) is a ref it MUST own its data since we must destroy it
|
||||||
# so then we have no choice but to use 'sinkArg'.
|
# so then we have no choice but to use 'sinkArg'.
|
||||||
let m = if mode == normal: normal
|
let isRefConstr = n.kind == nkObjConstr and n.typ.skipTypes(abstractInst).kind == tyRef
|
||||||
else: sinkArg
|
|
||||||
|
|
||||||
result = copyTree(n)
|
|
||||||
for i in ord(n.kind == nkClosure)..<n.len:
|
|
||||||
if n[i].kind == nkExprColonExpr:
|
|
||||||
result[i][1] = p(n[i][1], c, s, m)
|
|
||||||
elif n[i].kind == nkRange:
|
|
||||||
result[i][0] = p(n[i][0], c, s, m)
|
|
||||||
result[i][1] = p(n[i][1], c, s, m)
|
|
||||||
else:
|
|
||||||
result[i] = p(n[i], c, s, m)
|
|
||||||
of nkObjConstr:
|
|
||||||
# see also the remark about `nkTupleConstr`.
|
|
||||||
let t = n.typ.skipTypes(abstractInst)
|
|
||||||
let isRefConstr = t.kind == tyRef
|
|
||||||
let m = if isRefConstr: sinkArg
|
let m = if isRefConstr: sinkArg
|
||||||
elif mode == normal: normal
|
elif mode == normal: normal
|
||||||
else: sinkArg
|
else: sinkArg
|
||||||
|
|
||||||
result = copyTree(n)
|
result = copyTree(n)
|
||||||
for i in 1..<n.len:
|
for i in ord(n.kind in {nkObjConstr, nkClosure})..<n.len:
|
||||||
if n[i].kind == nkExprColonExpr:
|
if n[i].kind == nkExprColonExpr:
|
||||||
let field = lookupFieldAgain(t, n[i][0].sym)
|
result[i][1] = p(n[i][1], c, s, m)
|
||||||
if field != nil and sfCursor in field.flags:
|
elif n[i].kind == nkRange:
|
||||||
result[i][1] = p(n[i][1], c, s, normal)
|
result[i][0] = p(n[i][0], c, s, m)
|
||||||
else:
|
result[i][1] = p(n[i][1], c, s, m)
|
||||||
result[i][1] = p(n[i][1], c, s, m)
|
|
||||||
else:
|
else:
|
||||||
result[i] = p(n[i], c, s, m)
|
result[i] = p(n[i], c, s, m)
|
||||||
if mode == normal and isRefConstr:
|
if mode == normal and isRefConstr:
|
||||||
|
|
@ -822,43 +852,32 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||||
for it in n:
|
for it in n:
|
||||||
var ri = it[^1]
|
var ri = it[^1]
|
||||||
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||||
for i in 0..<it.len-2:
|
|
||||||
if it[i].kind == nkSym: s.locals.add it[i].sym
|
|
||||||
let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
|
let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
|
||||||
result.add p(x, c, s, consumed)
|
result.add p(x, c, s, consumed)
|
||||||
elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ):
|
elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ):
|
||||||
for j in 0..<it.len-2:
|
for j in 0..<it.len-2:
|
||||||
let v = skipPragmaExpr(it[j])
|
let v = it[j]
|
||||||
if v.kind == nkSym:
|
if v.kind == nkSym:
|
||||||
if sfCompileTime in v.sym.flags: continue
|
if sfCompileTime in v.sym.flags: continue
|
||||||
s.locals.add v.sym
|
|
||||||
pVarTopLevel(v, c, s, result)
|
pVarTopLevel(v, c, s, result)
|
||||||
if ri.kind != nkEmpty:
|
if ri.kind != nkEmpty:
|
||||||
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
result.add moveOrCopy(v, ri, c, s, isDecl = v.kind == nkSym)
|
||||||
elif ri.kind == nkEmpty and c.inLoop > 0:
|
elif ri.kind == nkEmpty and c.inLoop > 0:
|
||||||
let skipInit = v.kind == nkDotExpr and # Closure var
|
result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = v.kind == nkSym)
|
||||||
sfNoInit in v[1].sym.flags
|
|
||||||
if not skipInit:
|
|
||||||
result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {})
|
|
||||||
else: # keep the var but transform 'ri':
|
else: # keep the var but transform 'ri':
|
||||||
var v = copyNode(n)
|
var v = copyNode(n)
|
||||||
var itCopy = copyNode(it)
|
var itCopy = copyNode(it)
|
||||||
for j in 0..<it.len-1:
|
for j in 0..<it.len-1:
|
||||||
itCopy.add it[j]
|
itCopy.add it[j]
|
||||||
var flags = {sfSingleUsedTemp}
|
itCopy.add p(it[^1], c, s, normal)
|
||||||
if it.kind == nkIdentDefs and it.len == 3 and it[0].kind == nkSym and
|
|
||||||
sfGlobal in it[0].sym.flags:
|
|
||||||
flags.incl sfGlobal
|
|
||||||
itCopy.add p(it[^1], c, s, normal, tmpFlags = flags)
|
|
||||||
v.add itCopy
|
v.add itCopy
|
||||||
result.add v
|
result.add v
|
||||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
|
if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
|
||||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||||
cycleCheck(n, c)
|
cycleCheck(n, c)
|
||||||
assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
|
assert n[1].kind notin {nkAsgn, nkFastAsgn}
|
||||||
let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s)
|
||||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
|
|
||||||
elif isDiscriminantField(n[0]):
|
elif isDiscriminantField(n[0]):
|
||||||
result = c.genDiscriminantAsgn(s, n)
|
result = c.genDiscriminantAsgn(s, n)
|
||||||
else:
|
else:
|
||||||
|
|
@ -912,7 +931,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||||
for i in 1 ..< n.len:
|
for i in 1 ..< n.len:
|
||||||
result[i] = n[i]
|
result[i] = n[i]
|
||||||
if mode == sinkArg and hasDestructor(c, n.typ):
|
if mode == sinkArg and hasDestructor(c, n.typ):
|
||||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
|
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||||
s.wasMoved.add c.genWasMoved(n)
|
s.wasMoved.add c.genWasMoved(n)
|
||||||
else:
|
else:
|
||||||
result = passCopyToSink(result, c, s)
|
result = passCopyToSink(result, c, s)
|
||||||
|
|
@ -922,7 +941,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result[i] = p(n[i], c, s, normal)
|
result[i] = p(n[i], c, s, normal)
|
||||||
if mode == sinkArg and hasDestructor(c, n.typ):
|
if mode == sinkArg and hasDestructor(c, n.typ):
|
||||||
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
|
if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
|
||||||
# consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
|
# consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
|
||||||
# without the junk, hence 'c.genWasMoved(n)'
|
# without the junk, hence 'c.genWasMoved(n)'
|
||||||
# and not 'c.genWasMoved(result)':
|
# and not 'c.genWasMoved(result)':
|
||||||
|
|
@ -983,34 +1002,14 @@ proc sameLocation*(a, b: PNode): bool =
|
||||||
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
|
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
|
||||||
else: false
|
else: false
|
||||||
|
|
||||||
proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode =
|
||||||
# with side effects
|
|
||||||
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
|
|
||||||
temp.typ = ri[1].typ
|
|
||||||
var v = newNodeI(nkLetSection, ri[1].info)
|
|
||||||
let tempAsNode = newSymNode(temp)
|
|
||||||
|
|
||||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
|
||||||
vpart[0] = tempAsNode
|
|
||||||
vpart[1] = newNodeI(nkEmpty, tempAsNode.info)
|
|
||||||
vpart[2] = ri[1]
|
|
||||||
v.add(vpart)
|
|
||||||
|
|
||||||
var newAccess = copyNode(ri)
|
|
||||||
newAccess.add ri[0]
|
|
||||||
newAccess.add tempAsNode
|
|
||||||
|
|
||||||
var snk = c.genSink(dest, newAccess, flags)
|
|
||||||
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
|
|
||||||
|
|
||||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
|
|
||||||
if sameLocation(dest, ri):
|
if sameLocation(dest, ri):
|
||||||
# rule (self-assignment-removal):
|
# rule (self-assignment-removal):
|
||||||
result = newNodeI(nkEmpty, dest.info)
|
result = newNodeI(nkEmpty, dest.info)
|
||||||
elif isCursor(dest):
|
elif isCursor(dest):
|
||||||
case ri.kind:
|
case ri.kind:
|
||||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||||
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
|
template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl)
|
||||||
# We know the result will be a stmt so we use that fact to optimize
|
# We know the result will be a stmt so we use that fact to optimize
|
||||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1018,106 +1017,108 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||||
else:
|
else:
|
||||||
case ri.kind
|
case ri.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
if isUnpackedTuple(ri[0]):
|
if isUnpackedTuple(ri[0]):
|
||||||
# unpacking of tuple: take over the elements
|
# unpacking of tuple: take over the elements
|
||||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
|
||||||
if aliases(dest, ri) == no:
|
if aliases(dest, ri) == no:
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
if isAtom(ri[1]):
|
var snk = c.genSink(dest, ri, isDecl)
|
||||||
var snk = c.genSink(dest, ri, flags)
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
|
||||||
else:
|
|
||||||
result = genFieldAccessSideEffects(c, dest, ri, flags)
|
|
||||||
else:
|
else:
|
||||||
result = c.genSink(dest, destructiveMoveVar(ri, c, s), flags)
|
result = c.genSink(dest, destructiveMoveVar(ri, c, s), isDecl)
|
||||||
else:
|
else:
|
||||||
result = c.genCopy(dest, ri, flags)
|
result = c.genCopy(dest, ri)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
of nkBracket:
|
of nkBracket:
|
||||||
# array constructor
|
# array constructor
|
||||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||||
result = c.genCopy(dest, ri, flags)
|
result = c.genCopy(dest, ri)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
else:
|
else:
|
||||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
|
if isSinkParam(ri.sym) and isLastRead(ri, c):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
let snk = c.genSink(dest, ri, flags)
|
let snk = c.genSink(dest, ri, isDecl)
|
||||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
||||||
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
let snk = c.genSink(dest, ri, flags)
|
let snk = c.genSink(dest, ri, isDecl)
|
||||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
else:
|
else:
|
||||||
result = c.genCopy(dest, ri, flags)
|
result = c.genCopy(dest, ri)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv:
|
||||||
result = c.genSink(dest, p(ri, c, s, sinkArg), flags)
|
result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl)
|
||||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||||
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
|
template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl)
|
||||||
# We know the result will be a stmt so we use that fact to optimize
|
# We know the result will be a stmt so we use that fact to optimize
|
||||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||||
of nkRaiseStmt:
|
of nkRaiseStmt:
|
||||||
result = pRaiseStmt(ri, c, s)
|
result = pRaiseStmt(ri, c, s)
|
||||||
else:
|
else:
|
||||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||||
canBeMoved(c, dest.typ):
|
canBeMoved(c, dest.typ):
|
||||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||||
let snk = c.genSink(dest, ri, flags)
|
let snk = c.genSink(dest, ri, isDecl)
|
||||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||||
else:
|
else:
|
||||||
result = c.genCopy(dest, ri, flags)
|
result = c.genCopy(dest, ri)
|
||||||
result.add p(ri, c, s, consumed)
|
result.add p(ri, c, s, consumed)
|
||||||
c.finishCopy(result, dest, isFromSink = false)
|
c.finishCopy(result, dest, isFromSink = false)
|
||||||
|
|
||||||
when false:
|
proc computeUninit(c: var Con) =
|
||||||
proc computeUninit(c: var Con) =
|
if not c.uninitComputed:
|
||||||
if not c.uninitComputed:
|
c.uninitComputed = true
|
||||||
c.uninitComputed = true
|
c.uninit = initIntSet()
|
||||||
c.uninit = initIntSet()
|
var init = initIntSet()
|
||||||
var init = initIntSet()
|
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
|
||||||
discard initialized(c.g, pc = 0, init, c.uninit, int.high)
|
|
||||||
|
|
||||||
proc injectDefaultCalls(n: PNode, c: var Con) =
|
proc injectDefaultCalls(n: PNode, c: var Con) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkVarSection, nkLetSection:
|
of nkVarSection, nkLetSection:
|
||||||
for it in n:
|
for it in n:
|
||||||
if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
|
if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
|
||||||
computeUninit(c)
|
computeUninit(c)
|
||||||
for j in 0..<it.len-2:
|
for j in 0..<it.len-2:
|
||||||
let v = skipPragmaExpr(it[j])
|
let v = it[j]
|
||||||
doAssert v.kind == nkSym
|
doAssert v.kind == nkSym
|
||||||
if c.uninit.contains(v.sym.id):
|
if c.uninit.contains(v.sym.id):
|
||||||
it[^1] = genDefaultCall(v.sym.typ, c, v.info)
|
it[^1] = genDefaultCall(v.sym.typ, c, v.info)
|
||||||
break
|
break
|
||||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
|
||||||
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
for i in 0..<n.safeLen:
|
for i in 0..<n.safeLen:
|
||||||
injectDefaultCalls(n[i], c)
|
injectDefaultCalls(n[i], c)
|
||||||
|
|
||||||
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
|
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
|
||||||
when toDebug.len > 0:
|
when toDebug.len > 0:
|
||||||
shouldDebug = toDebug == owner.name.s or toDebug == "always"
|
shouldDebug = toDebug == owner.name.s or toDebug == "always"
|
||||||
if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
|
if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
|
||||||
return n
|
return n
|
||||||
var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo)
|
var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen)
|
||||||
|
dbg:
|
||||||
|
echo "\n### ", owner.name.s, ":\nCFG:"
|
||||||
|
echoCfg(c.g)
|
||||||
|
echo n
|
||||||
|
|
||||||
if optCursorInference in g.config.options:
|
if optCursorInference in g.config.options:
|
||||||
computeCursors(owner, n, g)
|
computeCursors(owner, n, g)
|
||||||
|
|
||||||
var scope = Scope(body: n)
|
computeLastReadsAndFirstWrites(c.g)
|
||||||
|
|
||||||
|
var scope: Scope
|
||||||
let body = p(n, c, scope, normal)
|
let body = p(n, c, scope, normal)
|
||||||
|
|
||||||
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
|
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ Platforms: """
|
||||||
netbsd: i386;amd64
|
netbsd: i386;amd64
|
||||||
openbsd: i386;amd64;arm;arm64
|
openbsd: i386;amd64;arm;arm64
|
||||||
dragonfly: i386;amd64
|
dragonfly: i386;amd64
|
||||||
crossos: amd64
|
|
||||||
haiku: i386;amd64
|
haiku: i386;amd64
|
||||||
android: i386;arm;arm64
|
android: i386;arm;arm64
|
||||||
nintendoswitch: arm64
|
nintendoswitch: arm64
|
||||||
|
|
@ -65,12 +64,11 @@ Files: "compiler"
|
||||||
Files: "doc"
|
Files: "doc"
|
||||||
Files: "doc/html"
|
Files: "doc/html"
|
||||||
Files: "tools"
|
Files: "tools"
|
||||||
Files: "tools/debug/nim-gdb.py"
|
Files: "tools/nim-gdb.py"
|
||||||
Files: "nimpretty"
|
Files: "nimpretty"
|
||||||
Files: "testament"
|
Files: "testament"
|
||||||
Files: "nimsuggest"
|
Files: "nimsuggest"
|
||||||
Files: "nimsuggest/tests/*.nim"
|
Files: "nimsuggest/tests/*.nim"
|
||||||
Files: "changelogs/*.md"
|
|
||||||
|
|
||||||
[Lib]
|
[Lib]
|
||||||
Files: "lib"
|
Files: "lib"
|
||||||
|
|
@ -121,7 +119,7 @@ Files: "bin/nim"
|
||||||
InstallScript: "yes"
|
InstallScript: "yes"
|
||||||
UninstallScript: "yes"
|
UninstallScript: "yes"
|
||||||
Files: "bin/nim-gdb"
|
Files: "bin/nim-gdb"
|
||||||
Files: "build_all.sh"
|
Files: "bin/nim-gdb.bash"
|
||||||
|
|
||||||
|
|
||||||
[InnoSetup]
|
[InnoSetup]
|
||||||
|
|
@ -143,5 +141,5 @@ shortDesc: "The Nim Compiler"
|
||||||
licenses: "bin/nim,MIT;lib/*,MIT;"
|
licenses: "bin/nim,MIT;lib/*,MIT;"
|
||||||
|
|
||||||
[nimble]
|
[nimble]
|
||||||
pkgName: "nim"
|
pkgName: "compiler"
|
||||||
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
|
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@
|
||||||
|
|
||||||
from math import trunc
|
from math import trunc
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Int128* = object
|
Int128* = object
|
||||||
udata: array[4, uint32]
|
udata: array[4, uint32]
|
||||||
|
|
@ -33,34 +30,39 @@ template high*(t: typedesc[Int128]): Int128 = Max
|
||||||
proc `$`*(a: Int128): string
|
proc `$`*(a: Int128): string
|
||||||
|
|
||||||
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
|
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
|
||||||
{.noSideEffect.}:
|
when T is bool: result.sdata(0) = int32(arg)
|
||||||
when T is bool: result.sdata(0) = int32(arg)
|
elif T is SomeUnsignedInt:
|
||||||
elif T is SomeUnsignedInt:
|
when sizeof(arg) <= 4:
|
||||||
when sizeof(arg) <= 4:
|
result.udata[0] = uint32(arg)
|
||||||
result.udata[0] = uint32(arg)
|
|
||||||
else:
|
|
||||||
result.udata[0] = uint32(arg and T(0xffffffff))
|
|
||||||
result.udata[1] = uint32(arg shr 32)
|
|
||||||
elif sizeof(arg) <= 4:
|
|
||||||
result.sdata(0) = int32(arg)
|
|
||||||
if arg < 0: # sign extend
|
|
||||||
result.sdata(1) = -1
|
|
||||||
result.sdata(2) = -1
|
|
||||||
result.sdata(3) = -1
|
|
||||||
else:
|
else:
|
||||||
let tmp = int64(arg)
|
result.udata[0] = uint32(arg and T(0xffffffff))
|
||||||
result.udata[0] = uint32(tmp and 0xffffffff)
|
result.udata[1] = uint32(arg shr 32)
|
||||||
result.sdata(1) = int32(tmp shr 32)
|
elif sizeof(arg) <= 4:
|
||||||
if arg < 0: # sign extend
|
result.sdata(0) = int32(arg)
|
||||||
result.sdata(2) = -1
|
if arg < 0: # sign extend
|
||||||
result.sdata(3) = -1
|
result.sdata(1) = -1
|
||||||
|
result.sdata(2) = -1
|
||||||
|
result.sdata(3) = -1
|
||||||
|
else:
|
||||||
|
let tmp = int64(arg)
|
||||||
|
result.udata[0] = uint32(tmp and 0xffffffff)
|
||||||
|
result.sdata(1) = int32(tmp shr 32)
|
||||||
|
if arg < 0: # sign extend
|
||||||
|
result.sdata(2) = -1
|
||||||
|
result.sdata(3) = -1
|
||||||
|
|
||||||
template isNegative(arg: Int128): bool =
|
template isNegative(arg: Int128): bool =
|
||||||
arg.sdata(3) < 0
|
arg.sdata(3) < 0
|
||||||
|
|
||||||
|
template isNegative(arg: int32): bool =
|
||||||
|
arg < 0
|
||||||
|
|
||||||
proc bitconcat(a, b: uint32): uint64 =
|
proc bitconcat(a, b: uint32): uint64 =
|
||||||
(uint64(a) shl 32) or uint64(b)
|
(uint64(a) shl 32) or uint64(b)
|
||||||
|
|
||||||
|
proc bitsplit(a: uint64): (uint32, uint32) =
|
||||||
|
(cast[uint32](a shr 32), cast[uint32](a))
|
||||||
|
|
||||||
proc toInt64*(arg: Int128): int64 =
|
proc toInt64*(arg: Int128): int64 =
|
||||||
if isNegative(arg):
|
if isNegative(arg):
|
||||||
assert(arg.sdata(3) == -1, "out of range")
|
assert(arg.sdata(3) == -1, "out of range")
|
||||||
|
|
@ -206,6 +208,12 @@ proc `==`*(a, b: Int128): bool =
|
||||||
if a.udata[3] != b.udata[3]: return false
|
if a.udata[3] != b.udata[3]: return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
proc inplaceBitnot(a: var Int128) =
|
||||||
|
a.udata[0] = not a.udata[0]
|
||||||
|
a.udata[1] = not a.udata[1]
|
||||||
|
a.udata[2] = not a.udata[2]
|
||||||
|
a.udata[3] = not a.udata[3]
|
||||||
|
|
||||||
proc bitnot*(a: Int128): Int128 =
|
proc bitnot*(a: Int128): Int128 =
|
||||||
result.udata[0] = not a.udata[0]
|
result.udata[0] = not a.udata[0]
|
||||||
result.udata[1] = not a.udata[1]
|
result.udata[1] = not a.udata[1]
|
||||||
|
|
@ -346,10 +354,23 @@ proc low64(a: Int128): uint64 =
|
||||||
bitconcat(a.udata[1], a.udata[0])
|
bitconcat(a.udata[1], a.udata[0])
|
||||||
|
|
||||||
proc `*`*(lhs, rhs: Int128): Int128 =
|
proc `*`*(lhs, rhs: Int128): Int128 =
|
||||||
let a32 = uint64(lhs.udata[1])
|
let
|
||||||
let a00 = uint64(lhs.udata[0])
|
a = cast[uint64](lhs.udata[0])
|
||||||
let b32 = uint64(rhs.udata[1])
|
b = cast[uint64](lhs.udata[1])
|
||||||
let b00 = uint64(rhs.udata[0])
|
c = cast[uint64](lhs.udata[2])
|
||||||
|
d = cast[uint64](lhs.udata[3])
|
||||||
|
|
||||||
|
e = cast[uint64](rhs.udata[0])
|
||||||
|
f = cast[uint64](rhs.udata[1])
|
||||||
|
g = cast[uint64](rhs.udata[2])
|
||||||
|
h = cast[uint64](rhs.udata[3])
|
||||||
|
|
||||||
|
|
||||||
|
let a32 = cast[uint64](lhs.udata[1])
|
||||||
|
let a00 = cast[uint64](lhs.udata[0])
|
||||||
|
let b32 = cast[uint64](rhs.udata[1])
|
||||||
|
let b00 = cast[uint64](rhs.udata[0])
|
||||||
|
|
||||||
result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
|
result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
|
||||||
result += toInt128(a32 * b00) shl 32
|
result += toInt128(a32 * b00) shl 32
|
||||||
result += toInt128(a00 * b32) shl 32
|
result += toInt128(a00 * b32) shl 32
|
||||||
|
|
@ -420,17 +441,17 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||||
result.remainder = dividend
|
result.remainder = dividend
|
||||||
|
|
||||||
proc `div`*(a, b: Int128): Int128 =
|
proc `div`*(a, b: Int128): Int128 =
|
||||||
let (a, _) = divMod(a, b)
|
let (a, b) = divMod(a, b)
|
||||||
return a
|
return a
|
||||||
|
|
||||||
proc `mod`*(a, b: Int128): Int128 =
|
proc `mod`*(a, b: Int128): Int128 =
|
||||||
let (_, b) = divMod(a, b)
|
let (a, b) = divMod(a, b)
|
||||||
return b
|
return b
|
||||||
|
|
||||||
proc addInt128*(result: var string; value: Int128) =
|
proc addInt128*(result: var string; value: Int128) =
|
||||||
let initialSize = result.len
|
let initialSize = result.len
|
||||||
if value == Zero:
|
if value == Zero:
|
||||||
result.add '0'
|
result.add "0"
|
||||||
elif value == low(Int128):
|
elif value == low(Int128):
|
||||||
result.add "-170141183460469231731687303715884105728"
|
result.add "-170141183460469231731687303715884105728"
|
||||||
else:
|
else:
|
||||||
|
|
@ -451,8 +472,6 @@ proc addInt128*(result: var string; value: Int128) =
|
||||||
j -= 1
|
j -= 1
|
||||||
|
|
||||||
proc `$`*(a: Int128): string =
|
proc `$`*(a: Int128): string =
|
||||||
# "-170141183460469231731687303715884105728".len == 41
|
|
||||||
result = newStringOfCap(41)
|
|
||||||
result.addInt128(a)
|
result.addInt128(a)
|
||||||
|
|
||||||
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@
|
||||||
import
|
import
|
||||||
ast, types, renderer, intsets
|
ast, types, renderer, intsets
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc canAlias(arg, ret: PType; marker: var IntSet): bool
|
proc canAlias(arg, ret: PType; marker: var IntSet): bool
|
||||||
|
|
||||||
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
|
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
|
||||||
|
|
@ -67,30 +64,19 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool =
|
||||||
else:
|
else:
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc isValueOnlyType(t: PType): bool =
|
proc isValueOnlyType(t: PType): bool =
|
||||||
# t doesn't contain pointers and references
|
# t doesn't contain pointers and references
|
||||||
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
|
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
|
||||||
result = not types.searchTypeFor(t, wrap)
|
result = not types.searchTypeFor(t, wrap)
|
||||||
|
|
||||||
proc canAlias*(arg, ret: PType): bool =
|
proc canAlias*(arg, ret: PType): bool =
|
||||||
if isValueOnlyType(arg):
|
if isValueOnlyType(arg):
|
||||||
# can alias only with addr(arg.x) and we don't care if it is not safe
|
# can alias only with unsafeAddr(arg.x) and we don't care if it is not safe
|
||||||
result = false
|
result = false
|
||||||
else:
|
else:
|
||||||
var marker = initIntSet()
|
var marker = initIntSet()
|
||||||
result = canAlias(arg, ret, marker)
|
result = canAlias(arg, ret, marker)
|
||||||
|
|
||||||
proc containsVariable(n: PNode): bool =
|
|
||||||
case n.kind
|
|
||||||
of nodesToIgnoreSet:
|
|
||||||
result = false
|
|
||||||
of nkSym:
|
|
||||||
result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
|
|
||||||
else:
|
|
||||||
for ch in n:
|
|
||||||
if containsVariable(ch): return true
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc checkIsolate*(n: PNode): bool =
|
proc checkIsolate*(n: PNode): bool =
|
||||||
if types.containsTyRef(n.typ):
|
if types.containsTyRef(n.typ):
|
||||||
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
||||||
|
|
@ -102,7 +88,7 @@ proc checkIsolate*(n: PNode): bool =
|
||||||
# XXX: as long as we don't update the analysis while examining arguments
|
# XXX: as long as we don't update the analysis while examining arguments
|
||||||
# we can do an early check of the return type, otherwise this is a
|
# we can do an early check of the return type, otherwise this is a
|
||||||
# bug and needs to be moved below
|
# bug and needs to be moved below
|
||||||
if tfNoSideEffect notin n[0].typ.flags:
|
if n[0].typ.flags * {tfGcSafe, tfNoSideEffect} == {}:
|
||||||
return false
|
return false
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
if checkIsolate(n[i]):
|
if checkIsolate(n[i]):
|
||||||
|
|
@ -110,11 +96,7 @@ proc checkIsolate*(n: PNode): bool =
|
||||||
else:
|
else:
|
||||||
let argType = n[i].typ
|
let argType = n[i].typ
|
||||||
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
|
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
|
||||||
if argType.canAlias(n.typ) or containsVariable(n[i]):
|
if argType.canAlias(n.typ):
|
||||||
# bug #19013: Alias information is not enough, we need to check for potential
|
|
||||||
# "overlaps". I claim the problem can only happen by reading again from a location
|
|
||||||
# that materialized which is only possible if a variable that contains a `ref`
|
|
||||||
# is involved.
|
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
of nkIfStmt, nkIfExpr:
|
of nkIfStmt, nkIfExpr:
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -19,13 +19,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||||
s, u: Rope
|
s, u: Rope
|
||||||
field: PSym
|
field: PSym
|
||||||
b: PNode
|
b: PNode
|
||||||
result = ""
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
if n.len == 1:
|
if n.len == 1:
|
||||||
result = genObjectFields(p, typ, n[0])
|
result = genObjectFields(p, typ, n[0])
|
||||||
else:
|
else:
|
||||||
s = ""
|
s = nil
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
if i > 0: s.add(", \L")
|
if i > 0: s.add(", \L")
|
||||||
s.add(genObjectFields(p, typ, n[i]))
|
s.add(genObjectFields(p, typ, n[i]))
|
||||||
|
|
@ -44,13 +44,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||||
s = genTypeInfo(p, field.typ)
|
s = genTypeInfo(p, field.typ)
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
b = n[i] # branch
|
b = n[i] # branch
|
||||||
u = ""
|
u = nil
|
||||||
case b.kind
|
case b.kind
|
||||||
of nkOfBranch:
|
of nkOfBranch:
|
||||||
if b.len < 2:
|
if b.len < 2:
|
||||||
internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
|
internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
|
||||||
for j in 0..<b.len - 1:
|
for j in 0..<b.len - 1:
|
||||||
if u != "": u.add(", ")
|
if u != nil: u.add(", ")
|
||||||
if b[j].kind == nkRange:
|
if b[j].kind == nkRange:
|
||||||
u.addf("[$1, $2]", [rope(getOrdValue(b[j][0])),
|
u.addf("[$1, $2]", [rope(getOrdValue(b[j][0])),
|
||||||
rope(getOrdValue(b[j][1]))])
|
rope(getOrdValue(b[j][1]))])
|
||||||
|
|
@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||||
of nkElse:
|
of nkElse:
|
||||||
u = rope(lengthOrd(p.config, field.typ))
|
u = rope(lengthOrd(p.config, field.typ))
|
||||||
else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
|
else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
|
||||||
if result != "": result.add(", \L")
|
if result != nil: result.add(", \L")
|
||||||
result.addf("[setConstr($1), $2]",
|
result.addf("[setConstr($1), $2]",
|
||||||
[u, genObjectFields(p, typ, lastSon(b))])
|
[u, genObjectFields(p, typ, lastSon(b))])
|
||||||
result = ("{kind: 3, offset: \"$1\", len: $3, " &
|
result = ("{kind: 3, offset: \"$1\", len: $3, " &
|
||||||
|
|
@ -84,7 +84,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
|
||||||
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
|
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
|
||||||
|
|
||||||
proc genTupleFields(p: PProc, typ: PType): Rope =
|
proc genTupleFields(p: PProc, typ: PType): Rope =
|
||||||
var s: Rope = ""
|
var s: Rope = nil
|
||||||
for i in 0..<typ.len:
|
for i in 0..<typ.len:
|
||||||
if i > 0: s.add(", \L")
|
if i > 0: s.add(", \L")
|
||||||
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
|
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
|
||||||
|
|
@ -102,7 +102,7 @@ proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
|
||||||
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
|
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
|
||||||
|
|
||||||
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||||
var s: Rope = ""
|
var s: Rope = nil
|
||||||
for i in 0..<typ.n.len:
|
for i in 0..<typ.n.len:
|
||||||
if (typ.n[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
|
if (typ.n[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
|
||||||
let field = typ.n[i].sym
|
let field = typ.n[i].sym
|
||||||
|
|
@ -133,16 +133,16 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||||
[result, rope(ord(t.kind))]
|
[result, rope(ord(t.kind))]
|
||||||
prepend(p.g.typeInfo, s)
|
prepend(p.g.typeInfo, s)
|
||||||
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray:
|
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
|
||||||
var s =
|
var s =
|
||||||
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
|
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||||
[result, rope(ord(t.kind))]
|
[result, rope(ord(t.kind))]
|
||||||
prepend(p.g.typeInfo, s)
|
prepend(p.g.typeInfo, s)
|
||||||
p.g.typeInfo.addf("$1.base = $2;$n",
|
p.g.typeInfo.addf("$1.base = $2;$n",
|
||||||
[result, genTypeInfo(p, t.lastSon)])
|
[result, genTypeInfo(p, t.lastSon)])
|
||||||
of tyArray:
|
of tyArray:
|
||||||
var s =
|
var s =
|
||||||
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
|
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||||
[result, rope(ord(t.kind))]
|
[result, rope(ord(t.kind))]
|
||||||
prepend(p.g.typeInfo, s)
|
prepend(p.g.typeInfo, s)
|
||||||
p.g.typeInfo.addf("$1.base = $2;$n",
|
p.g.typeInfo.addf("$1.base = $2;$n",
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,7 @@
|
||||||
import
|
import
|
||||||
intsets, strutils, options, ast, astalgo, msgs,
|
intsets, strutils, options, ast, astalgo, msgs,
|
||||||
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
|
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
|
||||||
transf, liftdestructors, typeallowed
|
transf, liftdestructors
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
discard """
|
discard """
|
||||||
The basic approach is that captured vars need to be put on the heap and
|
The basic approach is that captured vars need to be put on the heap and
|
||||||
|
|
@ -194,7 +191,9 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||||
s.typ.kind notin {tyStatic, tyTypeDesc}
|
s.typ.kind notin {tyStatic, tyTypeDesc}
|
||||||
|
|
||||||
proc illegalCapture(s: PSym): bool {.inline.} =
|
proc illegalCapture(s: PSym): bool {.inline.} =
|
||||||
result = classifyViewType(s.typ) != noView or s.kind == skResult
|
result = skipTypes(s.typ, abstractInst).kind in
|
||||||
|
{tyVar, tyOpenArray, tyVarargs, tyLent} or
|
||||||
|
s.kind == skResult
|
||||||
|
|
||||||
proc isInnerProc(s: PSym): bool =
|
proc isInnerProc(s: PSym): bool =
|
||||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
||||||
|
|
@ -245,13 +244,6 @@ proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen
|
||||||
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
|
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
|
||||||
owner.flags.incl sfInjectDestructors
|
owner.flags.incl sfInjectDestructors
|
||||||
|
|
||||||
proc genCreateEnv(env: PNode): PNode =
|
|
||||||
var c = newNodeIT(nkObjConstr, env.info, env.typ)
|
|
||||||
c.add newNodeIT(nkType, env.info, env.typ)
|
|
||||||
let e = copyTree(env)
|
|
||||||
e.flags.incl nfFirstWrite
|
|
||||||
result = newAsgnStmt(e, c)
|
|
||||||
|
|
||||||
proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||||
if liftingHarmful(g.config, owner): return n
|
if liftingHarmful(g.config, owner): return n
|
||||||
|
|
@ -275,19 +267,23 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||||
addVar(v, env)
|
addVar(v, env)
|
||||||
result.add(v)
|
result.add(v)
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
#result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
||||||
result.add genCreateEnv(env)
|
|
||||||
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
|
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
|
||||||
result.add makeClosure(g, idgen, iter, env, n.info)
|
result.add makeClosure(g, idgen, iter, env, n.info)
|
||||||
|
|
||||||
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
|
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
|
||||||
let envParam = getHiddenParam(g, owner)
|
let envParam = getHiddenParam(g, owner)
|
||||||
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||||
let field = addField(obj, s, g.cache, idgen)
|
addField(obj, s, g.cache, idgen)
|
||||||
|
|
||||||
var access = newSymNode(envParam)
|
var access = newSymNode(envParam)
|
||||||
assert obj.kind == tyObject
|
assert obj.kind == tyObject
|
||||||
result = rawIndirectAccess(access, field, s.info)
|
let field = getFieldFromObj(obj, s)
|
||||||
|
if field != nil:
|
||||||
|
result = rawIndirectAccess(access, field, s.info)
|
||||||
|
else:
|
||||||
|
localError(g.config, s.info, "internal error: cannot generate fresh variable")
|
||||||
|
result = access
|
||||||
|
|
||||||
# ------------------ new stuff -------------------------------------------
|
# ------------------ new stuff -------------------------------------------
|
||||||
|
|
||||||
|
|
@ -296,8 +292,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||||
if illegalCapture(s):
|
if illegalCapture(s):
|
||||||
localError(g.config, n.info,
|
localError(g.config, n.info,
|
||||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
" safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") %
|
||||||
" Consider using a <ref $2> which can be captured.") %
|
|
||||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||||
elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
|
elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
|
||||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||||
|
|
@ -441,7 +436,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||||
if innerProc:
|
if innerProc:
|
||||||
if s.isIterator: c.somethingToDo = true
|
if s.isIterator: c.somethingToDo = true
|
||||||
if not c.processed.containsOrIncl(s.id):
|
if not c.processed.containsOrIncl(s.id):
|
||||||
let body = transformBody(c.graph, c.idgen, s, useCache)
|
let body = transformBody(c.graph, c.idgen, s, cache = true)
|
||||||
detectCapturedVars(body, s, c)
|
detectCapturedVars(body, s, c)
|
||||||
let ow = s.skipGenericOwner
|
let ow = s.skipGenericOwner
|
||||||
if ow == owner:
|
if ow == owner:
|
||||||
|
|
@ -456,9 +451,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||||
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
||||||
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||||
else:
|
else:
|
||||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
addField(obj, s, c.graph.cache, c.idgen)
|
||||||
# direct or indirect dependency:
|
# direct or indirect dependency:
|
||||||
elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s):
|
elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
|
||||||
discard """
|
discard """
|
||||||
proc outer() =
|
proc outer() =
|
||||||
var x: int
|
var x: int
|
||||||
|
|
@ -478,7 +473,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||||
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
||||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
addField(obj, s, c.graph.cache, c.idgen)
|
||||||
# create required upFields:
|
# create required upFields:
|
||||||
var w = owner.skipGenericOwner
|
var w = owner.skipGenericOwner
|
||||||
if isInnerProc(w) or owner.isIterator:
|
if isInnerProc(w) or owner.isIterator:
|
||||||
|
|
@ -604,7 +599,7 @@ proc rawClosureCreation(owner: PSym;
|
||||||
addVar(v, unowned)
|
addVar(v, unowned)
|
||||||
|
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
result.add genCreateEnv(env)
|
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
|
||||||
if optOwnedRefs in d.graph.config.globalOptions:
|
if optOwnedRefs in d.graph.config.globalOptions:
|
||||||
let unowned = c.unownedEnvVars[owner.id]
|
let unowned = c.unownedEnvVars[owner.id]
|
||||||
assert unowned != nil
|
assert unowned != nil
|
||||||
|
|
@ -666,7 +661,7 @@ proc closureCreationForIter(iter: PNode;
|
||||||
var vs = newNodeI(nkVarSection, iter.info)
|
var vs = newNodeI(nkVarSection, iter.info)
|
||||||
addVar(vs, vnode)
|
addVar(vs, vnode)
|
||||||
result.add(vs)
|
result.add(vs)
|
||||||
result.add genCreateEnv(vnode)
|
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
|
||||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
|
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
|
||||||
|
|
||||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
||||||
|
|
@ -737,7 +732,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||||
# echo renderTree(s.getBody, {renderIds})
|
# echo renderTree(s.getBody, {renderIds})
|
||||||
let oldInContainer = c.inContainer
|
let oldInContainer = c.inContainer
|
||||||
c.inContainer = 0
|
c.inContainer = 0
|
||||||
var body = transformBody(d.graph, d.idgen, s, dontUseCache)
|
var body = transformBody(d.graph, d.idgen, s, cache = false)
|
||||||
body = liftCapturedVars(body, s, d, c)
|
body = liftCapturedVars(body, s, d, c)
|
||||||
if c.envVars.getOrDefault(s.id).isNil:
|
if c.envVars.getOrDefault(s.id).isNil:
|
||||||
s.transformedBody = body
|
s.transformedBody = body
|
||||||
|
|
@ -782,7 +777,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||||
n[1] = liftCapturedVars(n[1], owner, d, c)
|
n[1] = liftCapturedVars(n[1], owner, d, c)
|
||||||
if n[1].kind == nkClosure: result = n[1]
|
if n[1].kind == nkClosure: result = n[1]
|
||||||
of nkReturnStmt:
|
of nkReturnStmt:
|
||||||
if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
|
if n[0].kind in {nkAsgn, nkFastAsgn}:
|
||||||
# we have a `result = result` expression produced by the closure
|
# we have a `result = result` expression produced by the closure
|
||||||
# transform, let's not touch the LHS in order to make the lifting pass
|
# transform, let's not touch the LHS in order to make the lifting pass
|
||||||
# correct when `result` is lifted
|
# correct when `result` is lifted
|
||||||
|
|
@ -859,7 +854,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
|
||||||
fn.typ.callConv = oldCC
|
fn.typ.callConv = oldCC
|
||||||
|
|
||||||
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||||
idgen: IdGenerator, force: bool): PNode =
|
idgen: IdGenerator): PNode =
|
||||||
# XXX backend == backendJs does not suffice! The compiletime stuff needs
|
# XXX backend == backendJs does not suffice! The compiletime stuff needs
|
||||||
# the transformation even when compiling to JS ...
|
# the transformation even when compiling to JS ...
|
||||||
|
|
||||||
|
|
@ -868,7 +863,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||||
|
|
||||||
if body.kind == nkEmpty or (
|
if body.kind == nkEmpty or (
|
||||||
g.config.backend == backendJs and not isCompileTime) or
|
g.config.backend == backendJs and not isCompileTime) or
|
||||||
(fn.skipGenericOwner.kind != skModule and not force):
|
fn.skipGenericOwner.kind != skModule:
|
||||||
|
|
||||||
# ignore forward declaration:
|
# ignore forward declaration:
|
||||||
result = body
|
result = body
|
||||||
|
|
@ -951,7 +946,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||||
addVar(v, newSymNode(env))
|
addVar(v, newSymNode(env))
|
||||||
result.add(v)
|
result.add(v)
|
||||||
# add 'new' statement:
|
# add 'new' statement:
|
||||||
result.add genCreateEnv(env.newSymNode)
|
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
|
||||||
createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
|
createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
|
||||||
|
|
||||||
elif op.kind == nkStmtListExpr:
|
elif op.kind == nkStmtListExpr:
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||||
rememberSplit(splitComma)
|
rememberSplit(splitComma)
|
||||||
wrSpace em
|
wrSpace em
|
||||||
of openPars:
|
of openPars:
|
||||||
if tok.strongSpaceA and not em.endsInWhite and
|
if tok.strongSpaceA > 0 and not em.endsInWhite and
|
||||||
(not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
|
(not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
|
||||||
wrSpace em
|
wrSpace em
|
||||||
wr(em, $tok.tokType, ltSomeParLe)
|
wr(em, $tok.tokType, ltSomeParLe)
|
||||||
|
|
@ -528,7 +528,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||||
wr(em, $tok.tokType, ltOther)
|
wr(em, $tok.tokType, ltOther)
|
||||||
if not em.inquote: wrSpace(em)
|
if not em.inquote: wrSpace(em)
|
||||||
of tkOpr, tkDotDot:
|
of tkOpr, tkDotDot:
|
||||||
if em.inquote or (((not tok.strongSpaceA) and tok.strongSpaceB == tsNone) and
|
if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and
|
||||||
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
|
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
|
||||||
# bug #9504: remember to not spacify a keyword:
|
# bug #9504: remember to not spacify a keyword:
|
||||||
lastTokWasTerse = true
|
lastTokWasTerse = true
|
||||||
|
|
@ -538,7 +538,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||||
if not em.endsInWhite: wrSpace(em)
|
if not em.endsInWhite: wrSpace(em)
|
||||||
wr(em, tok.ident.s, ltOpr)
|
wr(em, tok.ident.s, ltOpr)
|
||||||
template isUnary(tok): bool =
|
template isUnary(tok): bool =
|
||||||
tok.strongSpaceB == tsNone and tok.strongSpaceA
|
tok.strongSpaceB == 0 and tok.strongSpaceA > 0
|
||||||
|
|
||||||
if not isUnary(tok):
|
if not isUnary(tok):
|
||||||
rememberSplit(splitBinary)
|
rememberSplit(splitBinary)
|
||||||
|
|
@ -551,15 +551,11 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||||
if not preventComment:
|
if not preventComment:
|
||||||
emitComment(em, tok, dontIndent = false)
|
emitComment(em, tok, dontIndent = false)
|
||||||
of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
|
of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
|
||||||
if not em.inquote:
|
let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
|
||||||
let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
|
if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
|
||||||
if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
|
em.lineSpan = countNewlines(lit)
|
||||||
em.lineSpan = countNewlines(lit)
|
if em.lineSpan > 0: calcCol(em, lit)
|
||||||
if em.lineSpan > 0: calcCol(em, lit)
|
wr em, lit, ltLit
|
||||||
wr em, lit, ltLit
|
|
||||||
else:
|
|
||||||
if endsInAlpha(em): wrSpace(em)
|
|
||||||
wr em, tok.literal, ltLit
|
|
||||||
of tkEof: discard
|
of tkEof: discard
|
||||||
else:
|
else:
|
||||||
let lit = if tok.ident != nil: tok.ident.s else: tok.literal
|
let lit = if tok.ident != nil: tok.ident.s else: tok.literal
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,18 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
# This lexer is handwritten for efficiency. I used an elegant buffering
|
# This scanner is handwritten for efficiency. I used an elegant buffering
|
||||||
# scheme which I have not seen anywhere else:
|
# scheme which I have not seen anywhere else:
|
||||||
# We guarantee that a whole line is in the buffer. Thus only when scanning
|
# We guarantee that a whole line is in the buffer. Thus only when scanning
|
||||||
# the \n or \r character we have to check whether we need to read in the next
|
# the \n or \r character we have to check whether we need to read in the next
|
||||||
# chunk. (\n or \r already need special handling for incrementing the line
|
# chunk. (\n or \r already need special handling for incrementing the line
|
||||||
# counter; choosing both \n and \r allows the lexer to properly read Unix,
|
# counter; choosing both \n and \r allows the scanner to properly read Unix,
|
||||||
# DOS or Macintosh text files, even when it is not the native format.
|
# DOS or Macintosh text files, even when it is not the native format.
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
||||||
wordrecg, lineinfos, pathutils, parseutils
|
wordrecg, lineinfos, pathutils, parseutils
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[assertions, formatfloat]
|
|
||||||
|
|
||||||
const
|
const
|
||||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||||
numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
|
numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
|
||||||
|
|
@ -93,22 +90,19 @@ type
|
||||||
# so that it is the correct default value
|
# so that it is the correct default value
|
||||||
base2, base8, base16
|
base2, base8, base16
|
||||||
|
|
||||||
TokenSpacing* = enum
|
Token* = object # a Nim token
|
||||||
tsNone, tsTrailing, tsEof
|
tokType*: TokType # the type of the token
|
||||||
|
indent*: int # the indentation; != -1 if the token has been
|
||||||
Token* = object # a Nim token
|
# preceded with indentation
|
||||||
tokType*: TokType # the type of the token
|
ident*: PIdent # the parsed identifier
|
||||||
indent*: int # the indentation; != -1 if the token has been
|
iNumber*: BiggestInt # the parsed integer literal
|
||||||
# preceded with indentation
|
fNumber*: BiggestFloat # the parsed floating point literal
|
||||||
ident*: PIdent # the parsed identifier
|
base*: NumericalBase # the numerical base; only valid for int
|
||||||
iNumber*: BiggestInt # the parsed integer literal
|
# or float literals
|
||||||
fNumber*: BiggestFloat # the parsed floating point literal
|
strongSpaceA*: int8 # leading spaces of an operator
|
||||||
base*: NumericalBase # the numerical base; only valid for int
|
strongSpaceB*: int8 # trailing spaces of an operator
|
||||||
# or float literals
|
literal*: string # the parsed (string) literal; and
|
||||||
strongSpaceA*: bool # leading spaces of an operator
|
# documentation comments are here too
|
||||||
strongSpaceB*: TokenSpacing # trailing spaces of an operator
|
|
||||||
literal*: string # the parsed (string) literal; and
|
|
||||||
# documentation comments are here too
|
|
||||||
line*, col*: int
|
line*, col*: int
|
||||||
when defined(nimpretty):
|
when defined(nimpretty):
|
||||||
offsetA*, offsetB*: int # used for pretty printing so that literals
|
offsetA*, offsetB*: int # used for pretty printing so that literals
|
||||||
|
|
@ -127,8 +121,6 @@ type
|
||||||
cache*: IdentCache
|
cache*: IdentCache
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
previousToken: TLineInfo
|
previousToken: TLineInfo
|
||||||
tokenEnd*: TLineInfo
|
|
||||||
previousTokenEnd*: TLineInfo
|
|
||||||
config*: ConfigRef
|
config*: ConfigRef
|
||||||
|
|
||||||
proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
|
proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
|
||||||
|
|
@ -178,7 +170,7 @@ proc initToken*(L: var Token) =
|
||||||
L.tokType = tkInvalid
|
L.tokType = tkInvalid
|
||||||
L.iNumber = 0
|
L.iNumber = 0
|
||||||
L.indent = 0
|
L.indent = 0
|
||||||
L.strongSpaceA = false
|
L.strongSpaceA = 0
|
||||||
L.literal = ""
|
L.literal = ""
|
||||||
L.fNumber = 0.0
|
L.fNumber = 0.0
|
||||||
L.base = base10
|
L.base = base10
|
||||||
|
|
@ -191,7 +183,7 @@ proc fillToken(L: var Token) =
|
||||||
L.tokType = tkInvalid
|
L.tokType = tkInvalid
|
||||||
L.iNumber = 0
|
L.iNumber = 0
|
||||||
L.indent = 0
|
L.indent = 0
|
||||||
L.strongSpaceA = false
|
L.strongSpaceA = 0
|
||||||
setLen(L.literal, 0)
|
setLen(L.literal, 0)
|
||||||
L.fNumber = 0.0
|
L.fNumber = 0.0
|
||||||
L.base = base10
|
L.base = base10
|
||||||
|
|
@ -510,11 +502,11 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||||
of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff
|
of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff
|
||||||
of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff
|
of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff
|
||||||
of tkFloat32Lit:
|
of tkFloat32Lit:
|
||||||
setNumber result.fNumber, (cast[ptr float32](addr(xi)))[]
|
setNumber result.fNumber, (cast[PFloat32](addr(xi)))[]
|
||||||
# note: this code is endian neutral!
|
# note: this code is endian neutral!
|
||||||
# XXX: Test this on big endian machine!
|
# XXX: Test this on big endian machine!
|
||||||
of tkFloat64Lit, tkFloatLit:
|
of tkFloat64Lit, tkFloatLit:
|
||||||
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
|
setNumber result.fNumber, (cast[PFloat64](addr(xi)))[]
|
||||||
else: internalError(L.config, getLineInfo(L), "getNumber")
|
else: internalError(L.config, getLineInfo(L), "getNumber")
|
||||||
|
|
||||||
# Bounds checks. Non decimal literals are allowed to overflow the range of
|
# Bounds checks. Non decimal literals are allowed to overflow the range of
|
||||||
|
|
@ -544,9 +536,9 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||||
try:
|
try:
|
||||||
len = parseBiggestUInt(result.literal, iNumber)
|
len = parseBiggestUInt(result.literal, iNumber)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise newException(OverflowDefect, "number out of range: " & result.literal)
|
raise newException(OverflowDefect, "number out of range: " & $result.literal)
|
||||||
if len != result.literal.len:
|
if len != result.literal.len:
|
||||||
raise newException(ValueError, "invalid integer: " & result.literal)
|
raise newException(ValueError, "invalid integer: " & $result.literal)
|
||||||
result.iNumber = cast[int64](iNumber)
|
result.iNumber = cast[int64](iNumber)
|
||||||
else:
|
else:
|
||||||
var iNumber: int64
|
var iNumber: int64
|
||||||
|
|
@ -554,9 +546,9 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||||
try:
|
try:
|
||||||
len = parseBiggestInt(result.literal, iNumber)
|
len = parseBiggestInt(result.literal, iNumber)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise newException(OverflowDefect, "number out of range: " & result.literal)
|
raise newException(OverflowDefect, "number out of range: " & $result.literal)
|
||||||
if len != result.literal.len:
|
if len != result.literal.len:
|
||||||
raise newException(ValueError, "invalid integer: " & result.literal)
|
raise newException(ValueError, "invalid integer: " & $result.literal)
|
||||||
result.iNumber = iNumber
|
result.iNumber = iNumber
|
||||||
|
|
||||||
# Explicit bounds checks.
|
# Explicit bounds checks.
|
||||||
|
|
@ -846,49 +838,6 @@ proc getCharacter(L: var Lexer; tok: var Token) =
|
||||||
lexMessage(L, errGenerated, "missing closing ' for character literal")
|
lexMessage(L, errGenerated, "missing closing ' for character literal")
|
||||||
tokenEndIgnore(tok, L.bufpos)
|
tokenEndIgnore(tok, L.bufpos)
|
||||||
|
|
||||||
const
|
|
||||||
UnicodeOperatorStartChars = {'\226', '\194', '\195'}
|
|
||||||
# the allowed unicode characters ("∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔")
|
|
||||||
# all start with one of these.
|
|
||||||
|
|
||||||
type
|
|
||||||
UnicodeOprPred = enum
|
|
||||||
Mul, Add
|
|
||||||
|
|
||||||
proc unicodeOprLen(buf: cstring; pos: int): (int8, UnicodeOprPred) =
|
|
||||||
template m(len): untyped = (int8(len), Mul)
|
|
||||||
template a(len): untyped = (int8(len), Add)
|
|
||||||
result = 0.m
|
|
||||||
case buf[pos]
|
|
||||||
of '\226':
|
|
||||||
if buf[pos+1] == '\136':
|
|
||||||
if buf[pos+2] == '\152': result = 3.m # ∘
|
|
||||||
elif buf[pos+2] == '\153': result = 3.m # ∙
|
|
||||||
elif buf[pos+2] == '\167': result = 3.m # ∧
|
|
||||||
elif buf[pos+2] == '\168': result = 3.a # ∨
|
|
||||||
elif buf[pos+2] == '\169': result = 3.m # ∩
|
|
||||||
elif buf[pos+2] == '\170': result = 3.a # ∪
|
|
||||||
elif buf[pos+1] == '\138':
|
|
||||||
if buf[pos+2] == '\147': result = 3.m # ⊓
|
|
||||||
elif buf[pos+2] == '\148': result = 3.a # ⊔
|
|
||||||
elif buf[pos+2] == '\149': result = 3.a # ⊕
|
|
||||||
elif buf[pos+2] == '\150': result = 3.a # ⊖
|
|
||||||
elif buf[pos+2] == '\151': result = 3.m # ⊗
|
|
||||||
elif buf[pos+2] == '\152': result = 3.m # ⊘
|
|
||||||
elif buf[pos+2] == '\153': result = 3.m # ⊙
|
|
||||||
elif buf[pos+2] == '\155': result = 3.m # ⊛
|
|
||||||
elif buf[pos+2] == '\158': result = 3.a # ⊞
|
|
||||||
elif buf[pos+2] == '\159': result = 3.a # ⊟
|
|
||||||
elif buf[pos+2] == '\160': result = 3.m # ⊠
|
|
||||||
elif buf[pos+2] == '\161': result = 3.m # ⊡
|
|
||||||
elif buf[pos+1] == '\152' and buf[pos+2] == '\133': result = 3.m # ★
|
|
||||||
of '\194':
|
|
||||||
if buf[pos+1] == '\177': result = 2.a # ±
|
|
||||||
of '\195':
|
|
||||||
if buf[pos+1] == '\151': result = 2.m # ×
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getSymbol(L: var Lexer, tok: var Token) =
|
proc getSymbol(L: var Lexer, tok: var Token) =
|
||||||
var h: Hash = 0
|
var h: Hash = 0
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
|
|
@ -897,7 +846,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||||
while true:
|
while true:
|
||||||
var c = L.buf[pos]
|
var c = L.buf[pos]
|
||||||
case c
|
case c
|
||||||
of 'a'..'z', '0'..'9':
|
of 'a'..'z', '0'..'9', '\x80'..'\xFF':
|
||||||
h = h !& ord(c)
|
h = h !& ord(c)
|
||||||
inc(pos)
|
inc(pos)
|
||||||
of 'A'..'Z':
|
of 'A'..'Z':
|
||||||
|
|
@ -911,16 +860,10 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||||
break
|
break
|
||||||
inc(pos)
|
inc(pos)
|
||||||
suspicious = true
|
suspicious = true
|
||||||
of '\x80'..'\xFF':
|
|
||||||
if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
h = h !& ord(c)
|
|
||||||
inc(pos)
|
|
||||||
else: break
|
else: break
|
||||||
tokenEnd(tok, pos-1)
|
tokenEnd(tok, pos-1)
|
||||||
h = !$h
|
h = !$h
|
||||||
tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
|
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||||
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
|
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
|
||||||
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
|
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
|
||||||
tok.tokType = tkSymbol
|
tok.tokType = tkSymbol
|
||||||
|
|
@ -934,7 +877,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||||
proc endOperator(L: var Lexer, tok: var Token, pos: int,
|
proc endOperator(L: var Lexer, tok: var Token, pos: int,
|
||||||
hash: Hash) {.inline.} =
|
hash: Hash) {.inline.} =
|
||||||
var h = !$hash
|
var h = !$hash
|
||||||
tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
|
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||||
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
|
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
|
||||||
else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon))
|
else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon))
|
||||||
L.bufpos = pos
|
L.bufpos = pos
|
||||||
|
|
@ -945,63 +888,42 @@ proc getOperator(L: var Lexer, tok: var Token) =
|
||||||
var h: Hash = 0
|
var h: Hash = 0
|
||||||
while true:
|
while true:
|
||||||
var c = L.buf[pos]
|
var c = L.buf[pos]
|
||||||
if c in OpChars:
|
if c notin OpChars: break
|
||||||
h = h !& ord(c)
|
h = h !& ord(c)
|
||||||
inc(pos)
|
inc(pos)
|
||||||
elif c in UnicodeOperatorStartChars:
|
|
||||||
let oprLen = unicodeOprLen(L.buf, pos)[0]
|
|
||||||
if oprLen == 0: break
|
|
||||||
for i in 0..<oprLen:
|
|
||||||
h = h !& ord(L.buf[pos])
|
|
||||||
inc pos
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
endOperator(L, tok, pos, h)
|
endOperator(L, tok, pos, h)
|
||||||
tokenEnd(tok, pos-1)
|
tokenEnd(tok, pos-1)
|
||||||
# advance pos but don't store it in L.bufpos so the next token (which might
|
# advance pos but don't store it in L.bufpos so the next token (which might
|
||||||
# be an operator too) gets the preceding spaces:
|
# be an operator too) gets the preceding spaces:
|
||||||
tok.strongSpaceB = tsNone
|
tok.strongSpaceB = 0
|
||||||
while L.buf[pos] == ' ':
|
while L.buf[pos] == ' ':
|
||||||
inc pos
|
inc pos
|
||||||
if tok.strongSpaceB != tsTrailing:
|
inc tok.strongSpaceB
|
||||||
tok.strongSpaceB = tsTrailing
|
|
||||||
if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||||
tok.strongSpaceB = tsEof
|
tok.strongSpaceB = -1
|
||||||
|
|
||||||
proc getPrecedence*(tok: Token): int =
|
proc getPrecedence*(tok: Token): int =
|
||||||
## Calculates the precedence of the given token.
|
## Calculates the precedence of the given token.
|
||||||
const
|
|
||||||
MulPred = 9
|
|
||||||
PlusPred = 8
|
|
||||||
case tok.tokType
|
case tok.tokType
|
||||||
of tkOpr:
|
of tkOpr:
|
||||||
let relevantChar = tok.ident.s[0]
|
let relevantChar = tok.ident.s[0]
|
||||||
|
|
||||||
# arrow like?
|
# arrow like?
|
||||||
if tok.ident.s.len > 1 and tok.ident.s[^1] == '>' and
|
if tok.ident.s.len > 1 and tok.ident.s[^1] == '>' and
|
||||||
tok.ident.s[^2] in {'-', '~', '='}: return 0
|
tok.ident.s[^2] in {'-', '~', '='}: return 1
|
||||||
|
|
||||||
template considerAsgn(value: untyped) =
|
template considerAsgn(value: untyped) =
|
||||||
result = if tok.ident.s[^1] == '=': 1 else: value
|
result = if tok.ident.s[^1] == '=': 1 else: value
|
||||||
|
|
||||||
case relevantChar
|
case relevantChar
|
||||||
of '$', '^': considerAsgn(10)
|
of '$', '^': considerAsgn(10)
|
||||||
of '*', '%', '/', '\\': considerAsgn(MulPred)
|
of '*', '%', '/', '\\': considerAsgn(9)
|
||||||
of '~': result = 8
|
of '~': result = 8
|
||||||
of '+', '-', '|': considerAsgn(PlusPred)
|
of '+', '-', '|': considerAsgn(8)
|
||||||
of '&': considerAsgn(7)
|
of '&': considerAsgn(7)
|
||||||
of '=', '<', '>', '!': result = 5
|
of '=', '<', '>', '!': result = 5
|
||||||
of '.': considerAsgn(6)
|
of '.': considerAsgn(6)
|
||||||
of '?': result = 2
|
of '?': result = 2
|
||||||
of UnicodeOperatorStartChars:
|
|
||||||
if tok.ident.s[^1] == '=':
|
|
||||||
result = 1
|
|
||||||
else:
|
|
||||||
let (len, pred) = unicodeOprLen(cstring(tok.ident.s), 0)
|
|
||||||
if len != 0:
|
|
||||||
result = if pred == Mul: MulPred else: PlusPred
|
|
||||||
else:
|
|
||||||
result = 2
|
|
||||||
else: considerAsgn(2)
|
else: considerAsgn(2)
|
||||||
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
||||||
of tkDotDot: result = 6
|
of tkDotDot: result = 6
|
||||||
|
|
@ -1153,7 +1075,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||||
proc skip(L: var Lexer, tok: var Token) =
|
proc skip(L: var Lexer, tok: var Token) =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
tokenBegin(tok, pos)
|
tokenBegin(tok, pos)
|
||||||
tok.strongSpaceA = false
|
tok.strongSpaceA = 0
|
||||||
when defined(nimpretty):
|
when defined(nimpretty):
|
||||||
var hasComment = false
|
var hasComment = false
|
||||||
var commentIndent = L.currLineIndent
|
var commentIndent = L.currLineIndent
|
||||||
|
|
@ -1164,8 +1086,7 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||||
case L.buf[pos]
|
case L.buf[pos]
|
||||||
of ' ':
|
of ' ':
|
||||||
inc(pos)
|
inc(pos)
|
||||||
if not tok.strongSpaceA:
|
inc(tok.strongSpaceA)
|
||||||
tok.strongSpaceA = true
|
|
||||||
of '\t':
|
of '\t':
|
||||||
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
|
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
|
||||||
inc(pos)
|
inc(pos)
|
||||||
|
|
@ -1187,7 +1108,7 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||||
pos = L.bufpos
|
pos = L.bufpos
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
tok.strongSpaceA = false
|
tok.strongSpaceA = 0
|
||||||
when defined(nimpretty):
|
when defined(nimpretty):
|
||||||
if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
|
if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
|
||||||
if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):
|
if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):
|
||||||
|
|
@ -1226,10 +1147,6 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||||
proc rawGetTok*(L: var Lexer, tok: var Token) =
|
proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||||
template atTokenEnd() {.dirty.} =
|
template atTokenEnd() {.dirty.} =
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
L.previousTokenEnd.line = L.tokenEnd.line
|
|
||||||
L.previousTokenEnd.col = L.tokenEnd.col
|
|
||||||
L.tokenEnd.line = tok.line.uint16
|
|
||||||
L.tokenEnd.col = getColNumber(L, L.bufpos).int16
|
|
||||||
# we attach the cursor to the last *strong* token
|
# we attach the cursor to the last *strong* token
|
||||||
if tok.tokType notin weakTokens:
|
if tok.tokType notin weakTokens:
|
||||||
L.previousToken.line = tok.line.uint16
|
L.previousToken.line = tok.line.uint16
|
||||||
|
|
@ -1250,15 +1167,10 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||||
var c = L.buf[L.bufpos]
|
var c = L.buf[L.bufpos]
|
||||||
tok.line = L.lineNumber
|
tok.line = L.lineNumber
|
||||||
tok.col = getColNumber(L, L.bufpos)
|
tok.col = getColNumber(L, L.bufpos)
|
||||||
if c in SymStartChars - {'r', 'R'} - UnicodeOperatorStartChars:
|
if c in SymStartChars - {'r', 'R'}:
|
||||||
getSymbol(L, tok)
|
getSymbol(L, tok)
|
||||||
else:
|
else:
|
||||||
case c
|
case c
|
||||||
of UnicodeOperatorStartChars:
|
|
||||||
if unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
|
||||||
getOperator(L, tok)
|
|
||||||
else:
|
|
||||||
getSymbol(L, tok)
|
|
||||||
of '#':
|
of '#':
|
||||||
scanComment(L, tok)
|
scanComment(L, tok)
|
||||||
of '*':
|
of '*':
|
||||||
|
|
@ -1366,11 +1278,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||||
getNumber(L, tok)
|
getNumber(L, tok)
|
||||||
let c = L.buf[L.bufpos]
|
let c = L.buf[L.bufpos]
|
||||||
if c in SymChars+{'_'}:
|
if c in SymChars+{'_'}:
|
||||||
if c in UnicodeOperatorStartChars and
|
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
|
||||||
unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
|
|
||||||
of '-':
|
of '-':
|
||||||
if L.buf[L.bufpos+1] in {'0'..'9'} and
|
if L.buf[L.bufpos+1] in {'0'..'9'} and
|
||||||
(L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
|
(L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
|
||||||
|
|
@ -1381,11 +1289,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||||
getNumber(L, tok)
|
getNumber(L, tok)
|
||||||
let c = L.buf[L.bufpos]
|
let c = L.buf[L.bufpos]
|
||||||
if c in SymChars+{'_'}:
|
if c in SymChars+{'_'}:
|
||||||
if c in UnicodeOperatorStartChars and
|
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
|
||||||
unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
|
|
||||||
else:
|
else:
|
||||||
getOperator(L, tok)
|
getOperator(L, tok)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module implements lifting for type-bound operations
|
## This module implements lifting for type-bound operations
|
||||||
## (``=sink``, ``=copy``, ``=destroy``, ``=deepCopy``).
|
## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
|
||||||
|
|
||||||
import modulegraphs, lineinfos, idents, ast, renderer, semdata,
|
import modulegraphs, lineinfos, idents, ast, renderer, semdata,
|
||||||
sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
|
sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
|
||||||
|
|
||||||
from trees import isCaseObj
|
from trees import isCaseObj
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TLiftCtx = object
|
TLiftCtx = object
|
||||||
g: ModuleGraph
|
g: ModuleGraph
|
||||||
|
|
@ -88,8 +85,6 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
let call = genBuiltin(c, mDefault, "default", x)
|
let call = genBuiltin(c, mDefault, "default", x)
|
||||||
call.typ = t
|
call.typ = t
|
||||||
body.add newAsgnStmt(x, call)
|
body.add newAsgnStmt(x, call)
|
||||||
elif c.kind == attachedWasMoved:
|
|
||||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
|
||||||
|
|
||||||
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
||||||
if x.kind == nkHiddenDeref:
|
if x.kind == nkHiddenDeref:
|
||||||
|
|
@ -147,11 +142,6 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = destroy
|
result = destroy
|
||||||
|
|
||||||
proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
|
||||||
result = newNodeIT(nkCall, x.info, op.typ[0])
|
|
||||||
result.add(newSymNode(op))
|
|
||||||
result.add genAddr(c, x)
|
|
||||||
|
|
||||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
|
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
@ -172,12 +162,9 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
|
||||||
# the value needs to be destroyed before we assign the selector
|
# the value needs to be destroyed before we assign the selector
|
||||||
# or the value is lost
|
# or the value is lost
|
||||||
let prevKind = c.kind
|
let prevKind = c.kind
|
||||||
let prevAddMemReset = c.addMemReset
|
|
||||||
c.kind = attachedDestructor
|
c.kind = attachedDestructor
|
||||||
c.addMemReset = true
|
|
||||||
fillBodyObj(c, n, body, x, y, enforceDefaultOp = false)
|
fillBodyObj(c, n, body, x, y, enforceDefaultOp = false)
|
||||||
c.kind = prevKind
|
c.kind = prevKind
|
||||||
c.addMemReset = prevAddMemReset
|
|
||||||
localEnforceDefaultOp = true
|
localEnforceDefaultOp = true
|
||||||
|
|
||||||
if c.kind != attachedDestructor:
|
if c.kind != attachedDestructor:
|
||||||
|
|
@ -431,16 +418,14 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
#result = addDestructorCall(c, t, body, x)
|
#result = addDestructorCall(c, t, body, x)
|
||||||
of attachedAsgn, attachedSink, attachedTrace:
|
of attachedAsgn, attachedSink, attachedTrace:
|
||||||
var op = getAttachedOp(c.g, t, c.kind)
|
var op = getAttachedOp(c.g, t, c.kind)
|
||||||
if op != nil and sfOverriden in op.flags:
|
|
||||||
if op.ast.isGenericRoutine:
|
|
||||||
# patch generic =trace:
|
|
||||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
|
||||||
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
|
|
||||||
|
|
||||||
result = considerAsgnOrSink(c, t, body, x, y, op)
|
result = considerAsgnOrSink(c, t, body, x, y, op)
|
||||||
if op != nil:
|
if op != nil:
|
||||||
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
|
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
|
||||||
|
of attachedDispose:
|
||||||
|
var op = getAttachedOp(c.g, t, c.kind)
|
||||||
|
result = considerAsgnOrSink(c, t, body, x, nil, op)
|
||||||
|
if op != nil:
|
||||||
|
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
|
||||||
of attachedDeepCopy:
|
of attachedDeepCopy:
|
||||||
let op = getAttachedOp(c.g, t, attachedDeepCopy)
|
let op = getAttachedOp(c.g, t, attachedDeepCopy)
|
||||||
if op != nil:
|
if op != nil:
|
||||||
|
|
@ -449,20 +434,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||||
body.add newDeepCopyCall(c, op, x, y)
|
body.add newDeepCopyCall(c, op, x, y)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
of attachedWasMoved:
|
|
||||||
var op = getAttachedOp(c.g, t, attachedWasMoved)
|
|
||||||
if op != nil and sfOverriden in op.flags:
|
|
||||||
|
|
||||||
if op.ast.isGenericRoutine:
|
|
||||||
# patch generic destructor:
|
|
||||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
|
||||||
setAttachedOp(c.g, c.idgen.module, t, attachedWasMoved, op)
|
|
||||||
|
|
||||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
|
||||||
onUse(c.info, op)
|
|
||||||
body.add genWasMovedCall(c, op, x)
|
|
||||||
result = true
|
|
||||||
|
|
||||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
|
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
|
||||||
temp.typ = getSysType(c.g, body.info, tyInt)
|
temp.typ = getSysType(c.g, body.info, tyInt)
|
||||||
|
|
@ -545,7 +516,9 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
if canFormAcycle(t.elemType):
|
if canFormAcycle(t.elemType):
|
||||||
# follow all elements:
|
# follow all elements:
|
||||||
forallElements(c, t, body, x, y)
|
forallElements(c, t, body, x, y)
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
of attachedDispose:
|
||||||
|
forallElements(c, t, body, x, y)
|
||||||
|
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||||
|
|
||||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||||
|
|
@ -553,7 +526,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
# operation here:
|
# operation here:
|
||||||
var t = t
|
var t = t
|
||||||
if t.assignment == nil or t.destructor == nil:
|
if t.assignment == nil or t.destructor == nil:
|
||||||
let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct})
|
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
|
||||||
let canon = c.g.canonTypes.getOrDefault(h)
|
let canon = c.g.canonTypes.getOrDefault(h)
|
||||||
if canon != nil: t = canon
|
if canon != nil: t = canon
|
||||||
|
|
||||||
|
|
@ -583,7 +556,11 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
if op == nil:
|
if op == nil:
|
||||||
return # protect from recursion
|
return # protect from recursion
|
||||||
body.add newHookCall(c, op, x, y)
|
body.add newHookCall(c, op, x, y)
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
of attachedDispose:
|
||||||
|
let op = getAttachedOp(c.g, t, c.kind)
|
||||||
|
if op == nil:
|
||||||
|
return # protect from recursion
|
||||||
|
body.add newHookCall(c, op, x, nil)
|
||||||
|
|
||||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -595,11 +572,10 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
doAssert t.destructor != nil
|
doAssert t.destructor != nil
|
||||||
moveCall.add destructorCall(c, t.destructor, x)
|
moveCall.add destructorCall(c, t.destructor, x)
|
||||||
body.add moveCall
|
body.add moveCall
|
||||||
of attachedDestructor:
|
of attachedDestructor, attachedDispose:
|
||||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||||
of attachedTrace:
|
of attachedTrace:
|
||||||
discard "strings are atomic and have no inner elements that are to trace"
|
discard "strings are atomic and have no inner elements that are to trace"
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
|
||||||
|
|
||||||
proc cyclicType*(t: PType): bool =
|
proc cyclicType*(t: PType): bool =
|
||||||
case t.kind
|
case t.kind
|
||||||
|
|
@ -633,11 +609,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
|
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
|
||||||
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
|
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
|
||||||
|
|
||||||
let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and
|
|
||||||
(not isPureObject(elemType)) and
|
|
||||||
tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags
|
|
||||||
# dynamic Acyclic refs need to use dyn decRef
|
|
||||||
|
|
||||||
let tmp =
|
let tmp =
|
||||||
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||||
declareTempOf(c, body, x)
|
declareTempOf(c, body, x)
|
||||||
|
|
@ -661,8 +632,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||||
else:
|
else:
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
||||||
elif isInheritableAcyclicRef:
|
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
|
|
||||||
else:
|
else:
|
||||||
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
|
@ -698,8 +667,16 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
# If the ref is polymorphic we have to account for this
|
# If the ref is polymorphic we have to account for this
|
||||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
|
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
|
||||||
#echo "can follow ", elemType, " static ", isFinal(elemType)
|
#echo "can follow ", elemType, " static ", isFinal(elemType)
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
of attachedDispose:
|
||||||
|
# this is crucial! dispose is like =destroy but we don't follow refs
|
||||||
|
# as that is dealt within the cycle collector.
|
||||||
|
if not isCyclic:
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
when false:
|
||||||
|
let cond = copyTree(x)
|
||||||
|
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||||
|
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
|
||||||
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
## Closures are really like refs except they always use a virtual destructor
|
## Closures are really like refs except they always use a virtual destructor
|
||||||
|
|
@ -748,7 +725,14 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
of attachedTrace:
|
of attachedTrace:
|
||||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
of attachedDispose:
|
||||||
|
# this is crucial! dispose is like =destroy but we don't follow refs
|
||||||
|
# as that is dealt within the cycle collector.
|
||||||
|
when false:
|
||||||
|
let cond = copyTree(xenv)
|
||||||
|
cond.typ = getSysType(c.g, xenv.info, tyBool)
|
||||||
|
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, xenv)
|
||||||
|
body.add genIf(c, cond, actions)
|
||||||
|
|
||||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
|
|
@ -772,8 +756,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
else:
|
else:
|
||||||
body.sons.insert(des, 0)
|
body.sons.insert(des, 0)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
of attachedTrace: discard
|
of attachedTrace, attachedDispose: discard
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
|
||||||
|
|
||||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
var actions = newNodeI(nkStmtList, c.info)
|
var actions = newNodeI(nkStmtList, c.info)
|
||||||
|
|
@ -798,8 +781,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
body.add genIf(c, x, actions)
|
body.add genIf(c, x, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
of attachedTrace: discard
|
of attachedTrace, attachedDispose: discard
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
|
||||||
|
|
||||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
if c.kind == attachedDeepCopy:
|
if c.kind == attachedDeepCopy:
|
||||||
|
|
@ -833,8 +815,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
else:
|
else:
|
||||||
body.sons.insert(des, 0)
|
body.sons.insert(des, 0)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
of attachedTrace: discard
|
of attachedTrace, attachedDispose: discard
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
|
||||||
|
|
||||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||||
|
|
@ -849,8 +830,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
of attachedDestructor:
|
of attachedDestructor:
|
||||||
body.add genIf(c, xx, actions)
|
body.add genIf(c, xx, actions)
|
||||||
of attachedDeepCopy: assert(false, "cannot happen")
|
of attachedDeepCopy: assert(false, "cannot happen")
|
||||||
of attachedTrace: discard
|
of attachedTrace, attachedDispose: discard
|
||||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
|
||||||
|
|
||||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case t.kind
|
case t.kind
|
||||||
|
|
@ -966,7 +946,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||||
|
|
||||||
result.typ = newProcType(info, nextTypeId(idgen), owner)
|
result.typ = newProcType(info, nextTypeId(idgen), owner)
|
||||||
result.typ.addParam dest
|
result.typ.addParam dest
|
||||||
if kind notin {attachedDestructor, attachedWasMoved}:
|
if kind notin {attachedDestructor, attachedDispose}:
|
||||||
result.typ.addParam src
|
result.typ.addParam src
|
||||||
|
|
||||||
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
|
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
|
||||||
|
|
@ -985,12 +965,6 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||||
incl result.flags, sfFromGeneric
|
incl result.flags, sfFromGeneric
|
||||||
incl result.flags, sfGeneratedOp
|
incl result.flags, sfGeneratedOp
|
||||||
|
|
||||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
|
||||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
|
||||||
let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
|
|
||||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
|
||||||
yy.typ = xx.typ
|
|
||||||
body.add newAsgnStmt(xx, yy)
|
|
||||||
|
|
||||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||||
|
|
@ -1006,7 +980,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
|
|
||||||
let dest = result.typ.n[1].sym
|
let dest = result.typ.n[1].sym
|
||||||
let d = newDeref(newSymNode(dest))
|
let d = newDeref(newSymNode(dest))
|
||||||
let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
let src = if kind in {attachedDestructor, attachedDispose}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||||
else: newSymNode(result.typ.n[2].sym)
|
else: newSymNode(result.typ.n[2].sym)
|
||||||
|
|
||||||
# register this operation already:
|
# register this operation already:
|
||||||
|
|
@ -1030,10 +1004,6 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||||
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
||||||
else:
|
else:
|
||||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy} and not lacksMTypeField(typ):
|
|
||||||
# bug #19205: Do not forget to also copy the hidden type field:
|
|
||||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
|
||||||
|
|
||||||
if not a.canRaise: incl result.flags, sfNeverRaises
|
if not a.canRaise: incl result.flags, sfNeverRaises
|
||||||
completePartialOp(g, idgen.module, typ, kind, result)
|
completePartialOp(g, idgen.module, typ, kind, result)
|
||||||
|
|
||||||
|
|
@ -1085,7 +1055,13 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||||
let op = getAttachedOp(g, t, kind)
|
let op = getAttachedOp(g, t, kind)
|
||||||
if op != nil and op.ast != nil and op.ast.isGenericRoutine:
|
if op != nil and op.ast != nil and op.ast.isGenericRoutine:
|
||||||
if t.typeInst != nil:
|
if t.typeInst != nil:
|
||||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen)
|
var a: TLiftCtx
|
||||||
|
a.info = info
|
||||||
|
a.g = g
|
||||||
|
a.kind = kind
|
||||||
|
a.c = c
|
||||||
|
a.idgen = idgen
|
||||||
|
|
||||||
let opInst = instantiateGeneric(a, op, t, t.typeInst)
|
let opInst = instantiateGeneric(a, op, t, t.typeInst)
|
||||||
if opInst.ast != nil:
|
if opInst.ast != nil:
|
||||||
patchBody(g, c, opInst.ast, info, a.idgen)
|
patchBody(g, c, opInst.ast, info, a.idgen)
|
||||||
|
|
@ -1107,7 +1083,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||||
|
|
||||||
let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct})
|
let h = sighashes.hashType(skipped, {CoType, CoConsiderOwned, CoDistinct})
|
||||||
var canon = g.canonTypes.getOrDefault(h)
|
var canon = g.canonTypes.getOrDefault(h)
|
||||||
if canon == nil:
|
if canon == nil:
|
||||||
g.canonTypes[h] = skipped
|
g.canonTypes[h] = skipped
|
||||||
|
|
@ -1120,23 +1096,23 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||||
# 4. We have a custom destructor.
|
# 4. We have a custom destructor.
|
||||||
# 5. We have a (custom) generic destructor.
|
# 5. We have a (custom) generic destructor.
|
||||||
|
|
||||||
# we do not generate '=trace' procs if we
|
# we do not generate '=trace' nor '=dispose' procs if we
|
||||||
# have the cycle detection disabled, saves code size.
|
# have the cycle detection disabled, saves code size.
|
||||||
let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace
|
let lastAttached = if g.config.selectedGC == gcOrc: attachedDispose
|
||||||
else: attachedSink
|
else: attachedSink
|
||||||
|
|
||||||
# bug #15122: We need to produce all prototypes before entering the
|
# bug #15122: We need to produce all prototypes before entering the
|
||||||
# mind boggling recursion. Hacks like these imply we should rewrite
|
# mind boggling recursion. Hacks like these imply we should rewrite
|
||||||
# this module.
|
# this module.
|
||||||
var generics: array[attachedWasMoved..attachedTrace, bool]
|
var generics: array[attachedDestructor..attachedDispose, bool]
|
||||||
for k in attachedWasMoved..lastAttached:
|
for k in attachedDestructor..lastAttached:
|
||||||
generics[k] = getAttachedOp(g, canon, k) != nil
|
generics[k] = getAttachedOp(g, canon, k) != nil
|
||||||
if not generics[k]:
|
if not generics[k]:
|
||||||
setAttachedOp(g, idgen.module, canon, k,
|
setAttachedOp(g, idgen.module, canon, k,
|
||||||
symPrototype(g, canon, canon.owner, k, info, idgen))
|
symPrototype(g, canon, canon.owner, k, info, idgen))
|
||||||
|
|
||||||
# we generate the destructor first so that other operators can depend on it:
|
# we generate the destructor first so that other operators can depend on it:
|
||||||
for k in attachedWasMoved..lastAttached:
|
for k in attachedDestructor..lastAttached:
|
||||||
if not generics[k]:
|
if not generics[k]:
|
||||||
discard produceSym(g, c, canon, k, info, idgen)
|
discard produceSym(g, c, canon, k, info, idgen)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -27,39 +27,26 @@ proc createDocLink*(urlSuffix: string): string =
|
||||||
|
|
||||||
type
|
type
|
||||||
TMsgKind* = enum
|
TMsgKind* = enum
|
||||||
# fatal errors
|
errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
|
||||||
errUnknown, errFatal, errInternal,
|
|
||||||
# non-fatal errors
|
|
||||||
errIllFormedAstX, errCannotOpenFile,
|
|
||||||
errXExpected,
|
errXExpected,
|
||||||
errRstMissingClosing,
|
errGridTableNotImplemented,
|
||||||
errRstGridTableNotImplemented,
|
errMarkdownIllformedTable,
|
||||||
errRstMarkdownIllformedTable,
|
errGeneralParseError,
|
||||||
errRstIllformedTable,
|
errNewSectionExpected,
|
||||||
errRstNewSectionExpected,
|
errInvalidDirectiveX,
|
||||||
errRstGeneralParseError,
|
errFootnoteMismatch,
|
||||||
errRstInvalidDirectiveX,
|
|
||||||
errRstInvalidField,
|
|
||||||
errRstFootnoteMismatch,
|
|
||||||
errRstSandboxedDirective,
|
|
||||||
errProveInit, # deadcode
|
errProveInit, # deadcode
|
||||||
errGenerated,
|
errGenerated,
|
||||||
errUser,
|
errUser,
|
||||||
# warnings
|
|
||||||
warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
|
warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
|
||||||
warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
|
warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
|
||||||
warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
|
warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
|
||||||
warnDotLikeOps = "DotLikeOps",
|
|
||||||
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
|
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
|
||||||
warnRstRedefinitionOfLabel = "RedefinitionOfLabel",
|
warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX",
|
||||||
warnRstUnknownSubstitutionX = "UnknownSubstitutionX",
|
warnLanguageXNotSupported = "LanguageXNotSupported",
|
||||||
warnRstAmbiguousLink = "AmbiguousLink",
|
warnFieldXNotSupported = "FieldXNotSupported",
|
||||||
warnRstBrokenLink = "BrokenLink",
|
warnRstStyle = "warnRstStyle", warnCommentXIgnored = "CommentXIgnored",
|
||||||
warnRstLanguageXNotSupported = "LanguageXNotSupported",
|
|
||||||
warnRstFieldXNotSupported = "FieldXNotSupported",
|
|
||||||
warnRstUnusedImportdoc = "UnusedImportdoc",
|
|
||||||
warnRstStyle = "warnRstStyle",
|
|
||||||
warnCommentXIgnored = "CommentXIgnored",
|
|
||||||
warnTypelessParam = "TypelessParam",
|
warnTypelessParam = "TypelessParam",
|
||||||
warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
|
warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
|
||||||
warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
|
warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
|
||||||
|
|
@ -69,40 +56,26 @@ type
|
||||||
warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
|
warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
|
||||||
warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
|
warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
|
||||||
warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
|
warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
|
||||||
warnLockLevel = "LockLevel", # deadcode
|
warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed",
|
||||||
warnResultShadowed = "ResultShadowed",
|
|
||||||
warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition",
|
warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition",
|
||||||
warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
|
warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
|
||||||
warnStrictNotNil = "StrictNotNil",
|
warnStrictNotNil = "StrictNotNil",
|
||||||
warnResultUsed = "ResultUsed",
|
warnResultUsed = "ResultUsed",
|
||||||
warnCannotOpen = "CannotOpen",
|
warnCannotOpen = "CannotOpen",
|
||||||
warnFileChanged = "FileChanged",
|
warnFileChanged = "FileChanged",
|
||||||
warnSuspiciousEnumConv = "EnumConv",
|
|
||||||
warnAnyEnumConv = "AnyEnumConv",
|
|
||||||
warnHoleEnumConv = "HoleEnumConv",
|
|
||||||
warnCstringConv = "CStringConv",
|
|
||||||
warnPtrToCstringConv = "PtrToCstringConv",
|
|
||||||
warnEffect = "Effect",
|
|
||||||
warnCastSizes = "CastSizes"
|
|
||||||
warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
|
|
||||||
warnUnnamedBreak = "UnnamedBreak",
|
|
||||||
warnStmtListLambda = "StmtListLambda",
|
|
||||||
warnBareExcept = "BareExcept",
|
|
||||||
warnUser = "User",
|
warnUser = "User",
|
||||||
# hints
|
|
||||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
hintSuccess = "Success", hintSuccessX = "SuccessX", hintBuildMode = "BuildMode",
|
||||||
hintCC = "CC",
|
hintCC = "CC",
|
||||||
hintLineTooLong = "LineTooLong",
|
hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed",
|
||||||
hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport",
|
|
||||||
hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
|
hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
|
||||||
hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
|
hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
|
||||||
hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintProcessingStmt = "ProcessingStmt", hintCodeBegin = "CodeBegin",
|
hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin",
|
||||||
hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path",
|
hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path",
|
||||||
hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name",
|
hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name",
|
||||||
hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
|
hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
|
||||||
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
||||||
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
||||||
hintAmbiguousEnum = "AmbiguousEnum",
|
|
||||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||||
|
|
@ -110,21 +83,16 @@ type
|
||||||
const
|
const
|
||||||
MsgKindToStr*: array[TMsgKind, string] = [
|
MsgKindToStr*: array[TMsgKind, string] = [
|
||||||
errUnknown: "unknown error",
|
errUnknown: "unknown error",
|
||||||
errFatal: "fatal error: $1",
|
|
||||||
errInternal: "internal error: $1",
|
errInternal: "internal error: $1",
|
||||||
errIllFormedAstX: "illformed AST: $1",
|
errIllFormedAstX: "illformed AST: $1",
|
||||||
errCannotOpenFile: "cannot open '$1'",
|
errCannotOpenFile: "cannot open '$1'",
|
||||||
errXExpected: "'$1' expected",
|
errXExpected: "'$1' expected",
|
||||||
errRstMissingClosing: "$1",
|
errGridTableNotImplemented: "grid table is not implemented",
|
||||||
errRstGridTableNotImplemented: "grid table is not implemented",
|
errMarkdownIllformedTable: "illformed delimiter row of a markdown table",
|
||||||
errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table",
|
errGeneralParseError: "general parse error",
|
||||||
errRstIllformedTable: "Illformed table: $1",
|
errNewSectionExpected: "new section expected $1",
|
||||||
errRstNewSectionExpected: "new section expected $1",
|
errInvalidDirectiveX: "invalid directive: '$1'",
|
||||||
errRstGeneralParseError: "general parse error",
|
errFootnoteMismatch: "number of footnotes and their references don't match: $1",
|
||||||
errRstInvalidDirectiveX: "invalid directive: '$1'",
|
|
||||||
errRstInvalidField: "invalid field: $1",
|
|
||||||
errRstFootnoteMismatch: "number of footnotes and their references don't match: $1",
|
|
||||||
errRstSandboxedDirective: "disabled directive: '$1'",
|
|
||||||
errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
|
errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
|
||||||
errGenerated: "$1",
|
errGenerated: "$1",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
|
|
@ -134,19 +102,15 @@ const
|
||||||
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
warnXmightNotBeenInit: "'$1' might not have been initialized",
|
||||||
warnDeprecated: "$1",
|
warnDeprecated: "$1",
|
||||||
warnConfigDeprecated: "config file '$1' is deprecated",
|
warnConfigDeprecated: "config file '$1' is deprecated",
|
||||||
warnDotLikeOps: "$1",
|
|
||||||
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
|
||||||
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
||||||
warnRstRedefinitionOfLabel: "redefinition of label '$1'",
|
warnRedefinitionOfLabel: "redefinition of label '$1'",
|
||||||
warnRstUnknownSubstitutionX: "unknown substitution '$1'",
|
warnUnknownSubstitutionX: "unknown substitution '$1'",
|
||||||
warnRstAmbiguousLink: "ambiguous doc link $1",
|
warnLanguageXNotSupported: "language '$1' not supported",
|
||||||
warnRstBrokenLink: "broken link '$1'",
|
warnFieldXNotSupported: "field '$1' not supported",
|
||||||
warnRstLanguageXNotSupported: "language '$1' not supported",
|
|
||||||
warnRstFieldXNotSupported: "field '$1' not supported",
|
|
||||||
warnRstUnusedImportdoc: "importdoc for '$1' is not used",
|
|
||||||
warnRstStyle: "RST style: $1",
|
warnRstStyle: "RST style: $1",
|
||||||
warnCommentXIgnored: "comment '$1' ignored",
|
warnCommentXIgnored: "comment '$1' ignored",
|
||||||
warnTypelessParam: "", # deadcode
|
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
|
||||||
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
|
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
|
||||||
warnWriteToForeignHeap: "write to foreign heap",
|
warnWriteToForeignHeap: "write to foreign heap",
|
||||||
warnUnsafeCode: "unsafe code: '$1'",
|
warnUnsafeCode: "unsafe code: '$1'",
|
||||||
|
|
@ -168,7 +132,7 @@ const
|
||||||
warnUninit: "use explicit initialization of '$1' for clarity",
|
warnUninit: "use explicit initialization of '$1' for clarity",
|
||||||
warnGcMem: "'$1' uses GC'ed memory",
|
warnGcMem: "'$1' uses GC'ed memory",
|
||||||
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
|
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
|
||||||
warnLockLevel: "$1", # deadcode
|
warnLockLevel: "$1",
|
||||||
warnResultShadowed: "Special variable 'result' is shadowed.",
|
warnResultShadowed: "Special variable 'result' is shadowed.",
|
||||||
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
|
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
|
||||||
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
||||||
|
|
@ -178,32 +142,20 @@ const
|
||||||
warnResultUsed: "used 'result' variable",
|
warnResultUsed: "used 'result' variable",
|
||||||
warnCannotOpen: "cannot open: $1",
|
warnCannotOpen: "cannot open: $1",
|
||||||
warnFileChanged: "file changed: $1",
|
warnFileChanged: "file changed: $1",
|
||||||
warnSuspiciousEnumConv: "$1",
|
|
||||||
warnAnyEnumConv: "$1",
|
|
||||||
warnHoleEnumConv: "$1",
|
|
||||||
warnCstringConv: "$1",
|
|
||||||
warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; this will become a compile time error in the future",
|
|
||||||
warnEffect: "$1",
|
|
||||||
warnCastSizes: "$1",
|
|
||||||
warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
|
|
||||||
warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
|
|
||||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
|
||||||
warnBareExcept: "$1",
|
|
||||||
warnUser: "$1",
|
warnUser: "$1",
|
||||||
hintSuccess: "operation successful: $#",
|
hintSuccess: "operation successful: $#",
|
||||||
# keep in sync with `testament.isSuccess`
|
# keep in sync with `testament.isSuccess`
|
||||||
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
hintSuccessX: "$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
||||||
|
hintBuildMode: "$1",
|
||||||
hintCC: "CC: $1",
|
hintCC: "CC: $1",
|
||||||
hintLineTooLong: "line too long",
|
hintLineTooLong: "line too long",
|
||||||
hintXDeclaredButNotUsed: "'$1' is declared but not used",
|
hintXDeclaredButNotUsed: "'$1' is declared but not used",
|
||||||
hintDuplicateModuleImport: "$1",
|
|
||||||
hintXCannotRaiseY: "$1",
|
hintXCannotRaiseY: "$1",
|
||||||
hintConvToBaseNotNeeded: "conversion to base object is not needed",
|
hintConvToBaseNotNeeded: "conversion to base object is not needed",
|
||||||
hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
|
hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
|
||||||
hintExprAlwaysX: "expression evaluates always to '$1'",
|
hintExprAlwaysX: "expression evaluates always to '$1'",
|
||||||
hintQuitCalled: "quit() called",
|
hintQuitCalled: "quit() called",
|
||||||
hintProcessing: "$1",
|
hintProcessing: "$1",
|
||||||
hintProcessingStmt: "$1",
|
|
||||||
hintCodeBegin: "generated code listing:",
|
hintCodeBegin: "generated code listing:",
|
||||||
hintCodeEnd: "end of listing",
|
hintCodeEnd: "end of listing",
|
||||||
hintConf: "used config file '$1'",
|
hintConf: "used config file '$1'",
|
||||||
|
|
@ -221,7 +173,6 @@ const
|
||||||
hintGCStats: "$1",
|
hintGCStats: "$1",
|
||||||
hintGlobalVar: "global variable declared here",
|
hintGlobalVar: "global variable declared here",
|
||||||
hintExpandMacro: "expanded macro: $1",
|
hintExpandMacro: "expanded macro: $1",
|
||||||
hintAmbiguousEnum: "$1",
|
|
||||||
hintUser: "$1",
|
hintUser: "$1",
|
||||||
hintUserRaw: "$1",
|
hintUserRaw: "$1",
|
||||||
hintExtendedContext: "$1",
|
hintExtendedContext: "$1",
|
||||||
|
|
@ -230,26 +181,26 @@ const
|
||||||
]
|
]
|
||||||
|
|
||||||
const
|
const
|
||||||
fatalMsgs* = {errUnknown..errInternal}
|
fatalMin* = errUnknown
|
||||||
|
fatalMax* = errInternal
|
||||||
errMin* = errUnknown
|
errMin* = errUnknown
|
||||||
errMax* = errUser
|
errMax* = errUser
|
||||||
warnMin* = warnCannotOpenFile
|
warnMin* = warnCannotOpenFile
|
||||||
warnMax* = pred(hintSuccess)
|
warnMax* = pred(hintSuccess)
|
||||||
hintMin* = hintSuccess
|
hintMin* = hintSuccess
|
||||||
hintMax* = high(TMsgKind)
|
hintMax* = high(TMsgKind)
|
||||||
rstWarnings* = {warnRstRedefinitionOfLabel..warnRstStyle}
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
|
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
|
||||||
TNoteKinds* = set[TNoteKind]
|
TNoteKinds* = set[TNoteKind]
|
||||||
|
|
||||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv}
|
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed}
|
||||||
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
|
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc}
|
||||||
result[1] = result[2] - {warnProveField, warnProveIndex,
|
result[1] = result[2] - {warnProveField, warnProveIndex,
|
||||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||||
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
|
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
|
||||||
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
|
result[0] = result[1] - {hintSuccessX, hintBuildMode, hintSuccess, hintConf,
|
||||||
hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
|
hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@
|
||||||
import std/strutils
|
import std/strutils
|
||||||
from std/sugar import dup
|
from std/sugar import dup
|
||||||
|
|
||||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
|
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs
|
||||||
export packages
|
|
||||||
|
|
||||||
const
|
const
|
||||||
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
|
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
|
||||||
|
|
@ -86,33 +85,24 @@ proc differ*(line: string, a, b: int, x: string): string =
|
||||||
result = y
|
result = y
|
||||||
|
|
||||||
proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||||
|
# operators stay as they are:
|
||||||
|
if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
|
||||||
|
if k in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||||
|
if s.typ != nil and s.typ.kind == tyTypeDesc: return
|
||||||
|
if {sfImportc, sfExportc} * s.flags != {}: return
|
||||||
|
if optStyleCheck notin s.options: return
|
||||||
let beau = beautifyName(s.name.s, k)
|
let beau = beautifyName(s.name.s, k)
|
||||||
if s.name.s != beau:
|
if s.name.s != beau:
|
||||||
lintReport(conf, info, beau, s.name.s)
|
lintReport(conf, info, beau, s.name.s)
|
||||||
|
|
||||||
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
|
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||||
## Check symbol definitions adhere to NEP1 style rules.
|
if {optStyleHint, optStyleError} * conf.globalOptions != {} and optStyleUsages notin conf.globalOptions:
|
||||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
nep1CheckDefImpl(conf, info, s, k)
|
||||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
|
||||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
|
||||||
ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
|
|
||||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
|
||||||
sym.kind != skResult and # ignore `result`
|
|
||||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
|
||||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
|
||||||
k notin {skType, skGenericParam} and # ignore types and generic params
|
|
||||||
(sym.typ == nil or sym.typ.kind != tyTypeDesc) and # ignore `typedesc`
|
|
||||||
{sfImportc, sfExportc} * sym.flags == {} and # ignore FFI
|
|
||||||
sfAnon notin sym.flags: # ignore if created by compiler
|
|
||||||
nep1CheckDefImpl(ctx.config, info, sym, k)
|
|
||||||
|
|
||||||
template styleCheckDef*(ctx: PContext; info: TLineInfo; s: PSym) =
|
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||||
## Check symbol definitions adhere to NEP1 style rules.
|
styleCheckDef(conf, info, s, s.kind)
|
||||||
styleCheckDef(ctx, info, s, s.kind)
|
template styleCheckDef*(conf: ConfigRef; s: PSym) =
|
||||||
|
styleCheckDef(conf, s.info, s, s.kind)
|
||||||
template styleCheckDef*(ctx: PContext; s: PSym) =
|
|
||||||
## Check symbol definitions adhere to NEP1 style rules.
|
|
||||||
styleCheckDef(ctx, s.info, s, s.kind)
|
|
||||||
|
|
||||||
proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
|
proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
|
||||||
let line = sourceLine(conf, info)
|
let line = sourceLine(conf, info)
|
||||||
|
|
@ -126,27 +116,23 @@ proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
|
||||||
let last = first+identLen(line, first)-1
|
let last = first+identLen(line, first)-1
|
||||||
result = differ(line, first, last, newName)
|
result = differ(line, first, last, newName)
|
||||||
|
|
||||||
proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||||
|
if info.fileIndex.int < 0: return
|
||||||
|
# we simply convert it to what it looks like in the definition
|
||||||
|
# for consistency
|
||||||
|
|
||||||
|
# operators stay as they are:
|
||||||
|
if s.kind == skTemp or s.name.s[0] notin Letters or sfAnon in s.flags:
|
||||||
|
return
|
||||||
|
|
||||||
let newName = s.name.s
|
let newName = s.name.s
|
||||||
let badName = differs(conf, info, newName)
|
let badName = differs(conf, info, newName)
|
||||||
if badName.len > 0:
|
if badName.len > 0:
|
||||||
lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s)))
|
# special rules for historical reasons
|
||||||
|
let forceHint = badName == "nnkArgList" and newName == "nnkArglist" or badName == "nnkArglist" and newName == "nnkArgList"
|
||||||
|
lintReport(conf, info, newName, badName, forceHint = forceHint, extraMsg = "".dup(addDeclaredLoc(conf, s)))
|
||||||
|
|
||||||
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
||||||
## Check symbol uses match their definition's style.
|
|
||||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
|
||||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
|
||||||
ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
|
|
||||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
|
||||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
|
||||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
|
||||||
styleCheckUseImpl(ctx.config, info, sym)
|
|
||||||
|
|
||||||
proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
|
||||||
let wanted = $w
|
let wanted = $w
|
||||||
if pragmaName != wanted:
|
if pragmaName != wanted:
|
||||||
lintReport(conf, info, wanted, pragmaName)
|
lintReport(conf, info, wanted, pragmaName)
|
||||||
|
|
||||||
template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
|
||||||
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
|
|
||||||
checkPragmaUseImpl(conf, info, w, pragmaName)
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
import
|
import
|
||||||
pathutils
|
pathutils
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/syncio
|
|
||||||
|
|
||||||
# support `useGnuReadline`, `useLinenoise` for backwards compatibility
|
# support `useGnuReadline`, `useLinenoise` for backwards compatibility
|
||||||
const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
|
const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
|
||||||
not defined(windows)
|
not defined(windows)
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# This module implements lookup helpers.
|
# This module implements lookup helpers.
|
||||||
import std/[algorithm, strutils]
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, idents, semdata, types, msgs, options,
|
intsets, ast, astalgo, idents, semdata, types, msgs, options,
|
||||||
renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs, sets
|
renderer, nimfix/prettybase, lineinfos, strutils,
|
||||||
|
modulegraphs, astmsgs
|
||||||
|
|
||||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
|
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
|
||||||
|
|
||||||
|
|
@ -48,11 +45,6 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||||
case x.kind
|
case x.kind
|
||||||
of nkIdent: id.add(x.ident.s)
|
of nkIdent: id.add(x.ident.s)
|
||||||
of nkSym: id.add(x.sym.name.s)
|
of nkSym: id.add(x.sym.name.s)
|
||||||
of nkSymChoices:
|
|
||||||
if x[0].kind == nkSym:
|
|
||||||
id.add(x[0].sym.name.s)
|
|
||||||
else:
|
|
||||||
handleError(n, origin)
|
|
||||||
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
|
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
|
||||||
else: handleError(n, origin)
|
else: handleError(n, origin)
|
||||||
result = getIdent(c.cache, id)
|
result = getIdent(c.cache, id)
|
||||||
|
|
@ -170,7 +162,6 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
|
||||||
# really iterate over all symbols in all the scopes. This is expensive
|
# really iterate over all symbols in all the scopes. This is expensive
|
||||||
# and only used by suggest.nim.
|
# and only used by suggest.nim.
|
||||||
var isLocal = true
|
var isLocal = true
|
||||||
|
|
||||||
var scopeN = 0
|
var scopeN = 0
|
||||||
for scope in allScopes(c.currentScope):
|
for scope in allScopes(c.currentScope):
|
||||||
if scope == c.topLevelScope: isLocal = false
|
if scope == c.topLevelScope: isLocal = false
|
||||||
|
|
@ -185,29 +176,16 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
|
||||||
assert s != nil
|
assert s != nil
|
||||||
yield (s, scopeN, isLocal)
|
yield (s, scopeN, isLocal)
|
||||||
|
|
||||||
iterator uniqueSyms*(c: PContext): (PSym, int, bool) =
|
|
||||||
## Like [allSyms] except only returns unique symbols (Uniqueness determined by line + name)
|
|
||||||
# Track seen symbols so we don't duplicate them.
|
|
||||||
# The int is for the symbols name, and line info is
|
|
||||||
# to be able to tell apart symbols with same name but on different lines
|
|
||||||
var seen = initHashSet[(TLineInfo, int)]()
|
|
||||||
for res in allSyms(c):
|
|
||||||
if not seen.containsOrIncl((res[0].info, res[0].name.id)):
|
|
||||||
yield res
|
|
||||||
|
|
||||||
|
|
||||||
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
|
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
|
||||||
var marked = initIntSet()
|
var marked = initIntSet()
|
||||||
var symSet = OverloadableSyms
|
|
||||||
result = nil
|
result = nil
|
||||||
block outer:
|
for im in c.imports.mitems:
|
||||||
for im in c.imports.mitems:
|
for s in symbols(im, marked, name, c.graph):
|
||||||
for s in symbols(im, marked, name, c.graph):
|
if result == nil:
|
||||||
if result == nil:
|
result = s
|
||||||
result = s
|
else:
|
||||||
elif s.kind notin symSet or result.kind notin symSet:
|
if s.kind notin OverloadableSyms or result.kind notin OverloadableSyms:
|
||||||
ambiguous = true
|
ambiguous = true
|
||||||
break outer
|
|
||||||
|
|
||||||
proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
|
proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
|
||||||
for scope in allScopes(c.currentScope):
|
for scope in allScopes(c.currentScope):
|
||||||
|
|
@ -228,35 +206,16 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||||
if i == limit: return
|
if i == limit: return
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||||
result = @[]
|
result = @[]
|
||||||
for scope in allScopes(c.currentScope):
|
for scope in allScopes(c.currentScope):
|
||||||
var ti: TIdentIter
|
var ti: TIdentIter
|
||||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||||
while candidate != nil:
|
while candidate != nil:
|
||||||
if candidate.kind in filter:
|
if candidate.kind in filter:
|
||||||
result.add candidate
|
if result.len == 0:
|
||||||
candidate = nextIdentIter(ti, scope.symbols)
|
|
||||||
|
|
||||||
if result.len == 0:
|
|
||||||
var marked = initIntSet()
|
|
||||||
for im in c.imports.mitems:
|
|
||||||
for s in symbols(im, marked, s, c.graph):
|
|
||||||
if s.kind in filter:
|
|
||||||
result.add s
|
|
||||||
|
|
||||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
|
||||||
result = @[]
|
|
||||||
block outer:
|
|
||||||
for scope in allScopes(c.currentScope):
|
|
||||||
var ti: TIdentIter
|
|
||||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
|
||||||
while candidate != nil:
|
|
||||||
if candidate.kind in filter:
|
|
||||||
result.add candidate
|
result.add candidate
|
||||||
# Break here, because further symbols encountered would be shadowed
|
candidate = nextIdentIter(ti, scope.symbols)
|
||||||
break outer
|
|
||||||
candidate = nextIdentIter(ti, scope.symbols)
|
|
||||||
|
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
var marked = initIntSet()
|
var marked = initIntSet()
|
||||||
|
|
@ -309,7 +268,6 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||||
var it: TTabIter
|
var it: TTabIter
|
||||||
var s = initTabIter(it, scope.symbols)
|
var s = initTabIter(it, scope.symbols)
|
||||||
var missingImpls = 0
|
var missingImpls = 0
|
||||||
var unusedSyms: seq[tuple[sym: PSym, key: string]]
|
|
||||||
while s != nil:
|
while s != nil:
|
||||||
if sfForward in s.flags and s.kind notin {skType, skModule}:
|
if sfForward in s.flags and s.kind notin {skType, skModule}:
|
||||||
# too many 'implementation of X' errors are annoying
|
# too many 'implementation of X' errors are annoying
|
||||||
|
|
@ -324,53 +282,39 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||||
# maybe they can be made skGenericParam as well.
|
# maybe they can be made skGenericParam as well.
|
||||||
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
|
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
|
||||||
s.typ.kind != tyGenericParam:
|
s.typ.kind != tyGenericParam:
|
||||||
unusedSyms.add (s, toFileLineCol(c.config, s.info))
|
message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
|
||||||
s = nextIter(it, scope.symbols)
|
s = nextIter(it, scope.symbols)
|
||||||
for (s, _) in sortedByIt(unusedSyms, it.key):
|
|
||||||
message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
|
|
||||||
|
|
||||||
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
||||||
conflictsWith: TLineInfo, note = errGenerated) =
|
conflictsWith: TLineInfo) =
|
||||||
## Emit a redefinition error if in non-interactive mode
|
## Emit a redefinition error if in non-interactive mode
|
||||||
if c.config.cmd != cmdInteractive:
|
if c.config.cmd != cmdInteractive:
|
||||||
localError(c.config, info, note,
|
localError(c.config, info,
|
||||||
"redefinition of '$1'; previous declaration here: $2" %
|
"redefinition of '$1'; previous declaration here: $2" %
|
||||||
[s, c.config $ conflictsWith])
|
[s, c.config $ conflictsWith])
|
||||||
|
|
||||||
# xxx pending bootstrap >= 1.4, replace all those overloads with a single one:
|
proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
|
||||||
# proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} =
|
let conflict = c.currentScope.addUniqueSym(sym)
|
||||||
proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
|
||||||
if sym.name.s == "_": return
|
|
||||||
let conflict = scope.addUniqueSym(sym)
|
|
||||||
if conflict != nil:
|
if conflict != nil:
|
||||||
if sym.kind == skModule and conflict.kind == skModule and
|
wrongRedefinition(c, info, sym.name.s, conflict.info)
|
||||||
sym.position == conflict.position:
|
|
||||||
# e.g.: import foo; import foo
|
|
||||||
# xxx we could refine this by issuing a different hint for the case
|
|
||||||
# where a duplicate import happens inside an include.
|
|
||||||
localError(c.config, info, hintDuplicateModuleImport,
|
|
||||||
"duplicate import of '$1'; previous import here: $2" %
|
|
||||||
[sym.name.s, c.config $ conflict.info])
|
|
||||||
else:
|
|
||||||
wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated)
|
|
||||||
|
|
||||||
proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) {.inline.} =
|
proc addDecl*(c: PContext, sym: PSym) =
|
||||||
addDeclAt(c, scope, sym, sym.info)
|
let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true)
|
||||||
|
if conflict != nil:
|
||||||
proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) {.inline.} =
|
wrongRedefinition(c, sym.info, sym.name.s, conflict.info)
|
||||||
addDeclAt(c, c.currentScope, sym, info)
|
|
||||||
|
|
||||||
proc addDecl*(c: PContext, sym: PSym) {.inline.} =
|
|
||||||
addDeclAt(c, c.currentScope, sym)
|
|
||||||
|
|
||||||
proc addPrelimDecl*(c: PContext, sym: PSym) =
|
proc addPrelimDecl*(c: PContext, sym: PSym) =
|
||||||
discard c.currentScope.addUniqueSym(sym)
|
discard c.currentScope.addUniqueSym(sym)
|
||||||
|
|
||||||
|
proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) =
|
||||||
|
let conflict = scope.addUniqueSym(sym)
|
||||||
|
if conflict != nil:
|
||||||
|
wrongRedefinition(c, sym.info, sym.name.s, conflict.info)
|
||||||
|
|
||||||
from ic / ic import addHidden
|
from ic / ic import addHidden
|
||||||
|
|
||||||
proc addInterfaceDeclAux(c: PContext, sym: PSym) =
|
proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) =
|
||||||
## adds symbol to the module for either private or public access.
|
if sfExported in sym.flags or forceExport:
|
||||||
if sfExported in sym.flags:
|
|
||||||
# add to interface:
|
# add to interface:
|
||||||
if c.module != nil: exportSym(c, sym)
|
if c.module != nil: exportSym(c, sym)
|
||||||
else: internalError(c.config, sym.info, "addInterfaceDeclAux")
|
else: internalError(c.config, sym.info, "addInterfaceDeclAux")
|
||||||
|
|
@ -380,19 +324,10 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
|
||||||
addHidden(c.encoder, c.packedRepr, sym)
|
addHidden(c.encoder, c.packedRepr, sym)
|
||||||
|
|
||||||
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
|
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
|
||||||
## adds a symbol on the scope and the interface if appropriate
|
|
||||||
addDeclAt(c, scope, sym)
|
addDeclAt(c, scope, sym)
|
||||||
if not scope.isShadowScope:
|
addInterfaceDeclAux(c, sym)
|
||||||
# adding into a non-shadow scope, we need to handle exports, etc
|
|
||||||
addInterfaceDeclAux(c, sym)
|
|
||||||
|
|
||||||
proc addInterfaceDecl*(c: PContext, sym: PSym) {.inline.} =
|
|
||||||
## adds a decl and the interface if appropriate
|
|
||||||
addInterfaceDeclAt(c, c.currentScope, sym)
|
|
||||||
|
|
||||||
proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
|
proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
|
||||||
## adds an symbol to the given scope, will check for and raise errors if it's
|
|
||||||
## a redefinition as opposed to an overload.
|
|
||||||
if fn.kind notin OverloadableSyms:
|
if fn.kind notin OverloadableSyms:
|
||||||
internalError(c.config, fn.info, "addOverloadableSymAt")
|
internalError(c.config, fn.info, "addOverloadableSymAt")
|
||||||
return
|
return
|
||||||
|
|
@ -402,33 +337,25 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
|
||||||
else:
|
else:
|
||||||
scope.addSym(fn)
|
scope.addSym(fn)
|
||||||
|
|
||||||
|
proc addInterfaceDecl*(c: PContext, sym: PSym) =
|
||||||
|
# it adds the symbol to the interface if appropriate
|
||||||
|
addDecl(c, sym)
|
||||||
|
addInterfaceDeclAux(c, sym)
|
||||||
|
|
||||||
proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
|
proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
|
||||||
## adds an overloadable symbol on the scope and the interface if appropriate
|
# it adds the symbol to the interface if appropriate
|
||||||
addOverloadableSymAt(c, scope, sym)
|
addOverloadableSymAt(c, scope, sym)
|
||||||
if not scope.isShadowScope:
|
addInterfaceDeclAux(c, sym)
|
||||||
# adding into a non-shadow scope, we need to handle exports, etc
|
|
||||||
addInterfaceDeclAux(c, sym)
|
|
||||||
|
|
||||||
proc openShadowScope*(c: PContext) =
|
proc openShadowScope*(c: PContext) =
|
||||||
## opens a shadow scope, just like any other scope except the depth is the
|
|
||||||
## same as the parent -- see `isShadowScope`.
|
|
||||||
c.currentScope = PScope(parent: c.currentScope,
|
c.currentScope = PScope(parent: c.currentScope,
|
||||||
symbols: newStrTable(),
|
symbols: newStrTable(),
|
||||||
depthLevel: c.scopeDepth)
|
depthLevel: c.scopeDepth)
|
||||||
|
|
||||||
proc closeShadowScope*(c: PContext) =
|
proc closeShadowScope*(c: PContext) =
|
||||||
## closes the shadow scope, but doesn't merge any of the symbols
|
c.closeScope
|
||||||
## Does not check for unused symbols or missing forward decls since a macro
|
|
||||||
## or template consumes this AST
|
|
||||||
rawCloseScope(c)
|
|
||||||
|
|
||||||
proc mergeShadowScope*(c: PContext) =
|
proc mergeShadowScope*(c: PContext) =
|
||||||
## close the existing scope and merge in all defined symbols, this will also
|
|
||||||
## trigger any export related code if this is into a non-shadow scope.
|
|
||||||
##
|
|
||||||
## Merges:
|
|
||||||
## shadow -> shadow: add symbols to the parent but check for redefinitions etc
|
|
||||||
## shadow -> non-shadow: the above, but also handle exports and all that
|
|
||||||
let shadowScope = c.currentScope
|
let shadowScope = c.currentScope
|
||||||
c.rawCloseScope
|
c.rawCloseScope
|
||||||
for sym in shadowScope.symbols:
|
for sym in shadowScope.symbols:
|
||||||
|
|
@ -453,7 +380,7 @@ type SpellCandidate = object
|
||||||
msg: string
|
msg: string
|
||||||
sym: PSym
|
sym: PSym
|
||||||
|
|
||||||
template toOrderTup(a: SpellCandidate): (int, int, string) =
|
template toOrderTup(a: SpellCandidate): auto =
|
||||||
# `dist` is first, to favor nearby matches
|
# `dist` is first, to favor nearby matches
|
||||||
# `depth` is next, to favor nearby enclosing scopes among ties
|
# `depth` is next, to favor nearby enclosing scopes among ties
|
||||||
# `sym.name.s` is last, to make the list ordered and deterministic among ties
|
# `sym.name.s` is last, to make the list ordered and deterministic among ties
|
||||||
|
|
@ -476,13 +403,12 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||||
let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
|
let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
|
||||||
var msg: string
|
var msg: string
|
||||||
msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s]
|
msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s]
|
||||||
|
addDeclaredLoc(msg, c.config, sym) # `msg` needed for deterministic ordering.
|
||||||
list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym)
|
list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym)
|
||||||
|
|
||||||
if list.len == 0: return
|
if list.len == 0: return
|
||||||
let e0 = list[0]
|
let e0 = list[0]
|
||||||
var
|
var count = 0
|
||||||
count = 0
|
|
||||||
last: PIdent = nil
|
|
||||||
while true:
|
while true:
|
||||||
# pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`.
|
# pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`.
|
||||||
if list.len == 0: break
|
if list.len == 0: break
|
||||||
|
|
@ -498,10 +424,8 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||||
elif count >= c.config.spellSuggestMax: break
|
elif count >= c.config.spellSuggestMax: break
|
||||||
if count == 0:
|
if count == 0:
|
||||||
result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
|
result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
|
||||||
if e.sym.name != last:
|
result.add e.msg
|
||||||
result.add e.msg
|
count.inc
|
||||||
count.inc
|
|
||||||
last = e.sym.name
|
|
||||||
|
|
||||||
proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym =
|
proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym =
|
||||||
var err = "ambiguous identifier: '" & s.name.s & "'"
|
var err = "ambiguous identifier: '" & s.name.s & "'"
|
||||||
|
|
@ -527,24 +451,17 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||||
var amb: bool
|
var amb: bool
|
||||||
discard errorUseQualifier(c, info, s, amb)
|
discard errorUseQualifier(c, info, s, amb)
|
||||||
|
|
||||||
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
|
||||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||||
var i = 0
|
var i = 0
|
||||||
for candidate in candidates:
|
for candidate in candidates:
|
||||||
if i == 0: err.add " -- $1 the following:\n" % prefix
|
if i == 0: err.add " -- use one of the following:\n"
|
||||||
else: err.add "\n"
|
else: err.add "\n"
|
||||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||||
err.add ": " & typeToString(candidate.typ)
|
err.add ": " & typeToString(candidate.typ)
|
||||||
inc i
|
inc i
|
||||||
localError(c.config, info, errGenerated, err)
|
localError(c.config, info, errGenerated, err)
|
||||||
|
|
||||||
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
|
||||||
var candidates = newSeq[PSym](choices.len)
|
|
||||||
let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
|
|
||||||
for i, n in choices:
|
|
||||||
candidates[i] = n.sym
|
|
||||||
errorUseQualifier(c, info, candidates, prefix)
|
|
||||||
|
|
||||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
||||||
var err = "undeclared identifier: '" & name & "'" & extra
|
var err = "undeclared identifier: '" & name & "'" & extra
|
||||||
if c.recursiveDep.len > 0:
|
if c.recursiveDep.len > 0:
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@ const
|
||||||
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
|
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
|
||||||
lineinfos
|
lineinfos
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc newDeref*(n: PNode): PNode {.inline.} =
|
proc newDeref*(n: PNode): PNode {.inline.} =
|
||||||
result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
|
result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
|
||||||
result.add n
|
result.add n
|
||||||
|
|
@ -74,20 +71,14 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P
|
||||||
let value = n.lastSon
|
let value = n.lastSon
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
|
||||||
var tempAsNode: PNode
|
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
||||||
let avoidTemp = value.kind == nkSym
|
owner, value.info, g.config.options)
|
||||||
if avoidTemp:
|
temp.typ = skipTypes(value.typ, abstractInst)
|
||||||
tempAsNode = value
|
incl(temp.flags, sfFromGeneric)
|
||||||
else:
|
|
||||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
|
||||||
owner, value.info, g.config.options)
|
|
||||||
temp.typ = skipTypes(value.typ, abstractInst)
|
|
||||||
incl(temp.flags, sfFromGeneric)
|
|
||||||
tempAsNode = newSymNode(temp)
|
|
||||||
|
|
||||||
var v = newNodeI(nkVarSection, value.info)
|
var v = newNodeI(nkVarSection, value.info)
|
||||||
if not avoidTemp:
|
let tempAsNode = newSymNode(temp)
|
||||||
v.addVar(tempAsNode, value)
|
v.addVar(tempAsNode, value)
|
||||||
result.add(v)
|
result.add(v)
|
||||||
|
|
||||||
for i in 0..<n.len-2:
|
for i in 0..<n.len-2:
|
||||||
|
|
@ -230,7 +221,7 @@ proc lookupInRecord(n: PNode, id: ItemId): PSym =
|
||||||
if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
|
if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
|
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) =
|
||||||
# because of 'gensym' support, we have to mangle the name with its ID.
|
# because of 'gensym' support, we have to mangle the name with its ID.
|
||||||
# This is hacky but the clean solution is much more complex than it looks.
|
# This is hacky but the clean solution is much more complex than it looks.
|
||||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
||||||
|
|
@ -241,11 +232,9 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym
|
||||||
assert t.kind != tyTyped
|
assert t.kind != tyTyped
|
||||||
propagateToOwner(obj, t)
|
propagateToOwner(obj, t)
|
||||||
field.position = obj.n.len
|
field.position = obj.n.len
|
||||||
# sfNoInit flag for skField is used in closureiterator codegen
|
field.flags = s.flags * {sfCursor}
|
||||||
field.flags = s.flags * {sfCursor, sfNoInit}
|
|
||||||
obj.n.add newSymNode(field)
|
obj.n.add newSymNode(field)
|
||||||
fieldCheck()
|
fieldCheck()
|
||||||
result = field
|
|
||||||
|
|
||||||
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
||||||
result = lookupInRecord(obj.n, s.itemId)
|
result = lookupInRecord(obj.n, s.itemId)
|
||||||
|
|
|
||||||
|
|
@ -13,26 +13,23 @@ when not defined(nimcore):
|
||||||
{.error: "nimcore MUST be defined for Nim's core tooling".}
|
{.error: "nimcore MUST be defined for Nim's core tooling".}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[strutils, os, times, tables, sha1, with, json],
|
llstream, strutils, os, ast, lexer, syntaxes, options, msgs,
|
||||||
llstream, ast, lexer, syntaxes, options, msgs,
|
condsyms, times,
|
||||||
condsyms,
|
sem, idents, passes, extccomp,
|
||||||
idents, extccomp,
|
cgen, json, nversion,
|
||||||
cgen, nversion,
|
platform, nimconf, passaux, depends, vm,
|
||||||
platform, nimconf, depends,
|
|
||||||
modules,
|
modules,
|
||||||
modulegraphs, lineinfos, pathutils, vmprofiler
|
modulegraphs, tables, lineinfos, pathutils, vmprofiler, std/[sha1, with]
|
||||||
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[syncio, assertions]
|
|
||||||
|
|
||||||
import ic / [cbackend, integrity, navigator]
|
import ic / [cbackend, integrity, navigator]
|
||||||
from ic / ic import rodViewer
|
from ic / ic import rodViewer
|
||||||
|
|
||||||
import pipelines
|
|
||||||
|
|
||||||
when not defined(leanCompiler):
|
when not defined(leanCompiler):
|
||||||
import docgen
|
import jsgen, docgen, docgen2
|
||||||
|
|
||||||
|
proc semanticPasses(g: ModuleGraph) =
|
||||||
|
registerPass g, verbosePass
|
||||||
|
registerPass g, semPass
|
||||||
|
|
||||||
proc writeDepsFile(g: ModuleGraph) =
|
proc writeDepsFile(g: ModuleGraph) =
|
||||||
let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
|
let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
|
||||||
|
|
@ -45,39 +42,13 @@ proc writeDepsFile(g: ModuleGraph) =
|
||||||
f.writeLine(toFullPath(g.config, k))
|
f.writeLine(toFullPath(g.config, k))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
proc writeCMakeDepsFile(conf: ConfigRef) =
|
|
||||||
## write a list of C files for build systems like CMake.
|
|
||||||
## only updated when the C file list changes.
|
|
||||||
let fname = getNimcacheDir(conf) / conf.outFile.changeFileExt("cdeps")
|
|
||||||
# generate output files list
|
|
||||||
var cfiles: seq[string] = @[]
|
|
||||||
for it in conf.toCompile: cfiles.add(it.cname.string)
|
|
||||||
let fileset = cfiles.toCountTable()
|
|
||||||
# read old cfiles list
|
|
||||||
var fl: File
|
|
||||||
var prevset = initCountTable[string]()
|
|
||||||
if open(fl, fname.string, fmRead):
|
|
||||||
for line in fl.lines: prevset.inc(line)
|
|
||||||
fl.close()
|
|
||||||
# write cfiles out
|
|
||||||
if fileset != prevset:
|
|
||||||
fl = open(fname.string, fmWrite)
|
|
||||||
for line in cfiles: fl.writeLine(line)
|
|
||||||
fl.close()
|
|
||||||
|
|
||||||
proc commandGenDepend(graph: ModuleGraph) =
|
proc commandGenDepend(graph: ModuleGraph) =
|
||||||
setPipeLinePass(graph, GenDependPass)
|
semanticPasses(graph)
|
||||||
compilePipelineProject(graph)
|
registerPass(graph, gendependPass)
|
||||||
|
compileProject(graph)
|
||||||
let project = graph.config.projectFull
|
let project = graph.config.projectFull
|
||||||
writeDepsFile(graph)
|
writeDepsFile(graph)
|
||||||
generateDot(graph, project)
|
generateDot(graph, project)
|
||||||
|
|
||||||
# dot in graphivz tool kit is required
|
|
||||||
let graphvizDotPath = findExe("dot")
|
|
||||||
if graphvizDotPath.len == 0:
|
|
||||||
quit("gendepend: Graphviz's tool dot is required," &
|
|
||||||
"see https://graphviz.org/download for downloading")
|
|
||||||
|
|
||||||
execExternalProgram(graph.config, "dot -Tpng -o" &
|
execExternalProgram(graph.config, "dot -Tpng -o" &
|
||||||
changeFileExt(project, "png").string &
|
changeFileExt(project, "png").string &
|
||||||
' ' & changeFileExt(project, "dot").string)
|
' ' & changeFileExt(project, "dot").string)
|
||||||
|
|
@ -86,13 +57,8 @@ proc commandCheck(graph: ModuleGraph) =
|
||||||
let conf = graph.config
|
let conf = graph.config
|
||||||
conf.setErrorMaxHighMaybe
|
conf.setErrorMaxHighMaybe
|
||||||
defineSymbol(conf.symbols, "nimcheck")
|
defineSymbol(conf.symbols, "nimcheck")
|
||||||
if optWasNimscript in conf.globalOptions:
|
semanticPasses(graph) # use an empty backend for semantic checking only
|
||||||
defineSymbol(conf.symbols, "nimscript")
|
compileProject(graph)
|
||||||
defineSymbol(conf.symbols, "nimconfig")
|
|
||||||
elif conf.backend == backendJs:
|
|
||||||
setTarget(conf.target, osJS, cpuJS)
|
|
||||||
setPipeLinePass(graph, SemPass)
|
|
||||||
compilePipelineProject(graph)
|
|
||||||
|
|
||||||
if conf.symbolFiles != disabledSf:
|
if conf.symbolFiles != disabledSf:
|
||||||
case conf.ideCmd
|
case conf.ideCmd
|
||||||
|
|
@ -106,20 +72,22 @@ when not defined(leanCompiler):
|
||||||
proc commandDoc2(graph: ModuleGraph; ext: string) =
|
proc commandDoc2(graph: ModuleGraph; ext: string) =
|
||||||
handleDocOutputOptions graph.config
|
handleDocOutputOptions graph.config
|
||||||
graph.config.setErrorMaxHighMaybe
|
graph.config.setErrorMaxHighMaybe
|
||||||
|
semanticPasses(graph)
|
||||||
case ext:
|
case ext:
|
||||||
of TexExt:
|
of TexExt: registerPass(graph, docgen2TexPass)
|
||||||
setPipeLinePass(graph, Docgen2TexPass)
|
of JsonExt: registerPass(graph, docgen2JsonPass)
|
||||||
of JsonExt:
|
of HtmlExt: registerPass(graph, docgen2Pass)
|
||||||
setPipeLinePass(graph, Docgen2JsonPass)
|
|
||||||
of HtmlExt:
|
|
||||||
setPipeLinePass(graph, Docgen2Pass)
|
|
||||||
else: doAssert false, $ext
|
else: doAssert false, $ext
|
||||||
compilePipelineProject(graph)
|
compileProject(graph)
|
||||||
|
finishDoc2Pass(graph.config.projectName)
|
||||||
|
|
||||||
proc commandCompileToC(graph: ModuleGraph) =
|
proc commandCompileToC(graph: ModuleGraph) =
|
||||||
let conf = graph.config
|
let conf = graph.config
|
||||||
extccomp.initVars(conf)
|
extccomp.initVars(conf)
|
||||||
|
semanticPasses(graph)
|
||||||
if conf.symbolFiles == disabledSf:
|
if conf.symbolFiles == disabledSf:
|
||||||
|
registerPass(graph, cgenPass)
|
||||||
|
|
||||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
||||||
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
|
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
|
||||||
# nothing changed
|
# nothing changed
|
||||||
|
|
@ -129,11 +97,7 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||||
if not extccomp.ccHasSaneOverflow(conf):
|
if not extccomp.ccHasSaneOverflow(conf):
|
||||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||||
|
|
||||||
if conf.symbolFiles == disabledSf:
|
compileProject(graph)
|
||||||
setPipeLinePass(graph, CgenPass)
|
|
||||||
else:
|
|
||||||
setPipeLinePass(graph, SemPass)
|
|
||||||
compilePipelineProject(graph)
|
|
||||||
if graph.config.errorCounter > 0:
|
if graph.config.errorCounter > 0:
|
||||||
return # issue #9933
|
return # issue #9933
|
||||||
if conf.symbolFiles == disabledSf:
|
if conf.symbolFiles == disabledSf:
|
||||||
|
|
@ -152,8 +116,6 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||||
extccomp.writeJsonBuildInstructions(conf)
|
extccomp.writeJsonBuildInstructions(conf)
|
||||||
if optGenScript in graph.config.globalOptions:
|
if optGenScript in graph.config.globalOptions:
|
||||||
writeDepsFile(graph)
|
writeDepsFile(graph)
|
||||||
if optGenCDeps in graph.config.globalOptions:
|
|
||||||
writeCMakeDepsFile(conf)
|
|
||||||
|
|
||||||
proc commandJsonScript(graph: ModuleGraph) =
|
proc commandJsonScript(graph: ModuleGraph) =
|
||||||
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
|
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
|
||||||
|
|
@ -166,27 +128,33 @@ proc commandCompileToJS(graph: ModuleGraph) =
|
||||||
conf.exc = excCpp
|
conf.exc = excCpp
|
||||||
setTarget(conf.target, osJS, cpuJS)
|
setTarget(conf.target, osJS, cpuJS)
|
||||||
defineSymbol(conf.symbols, "ecmascript") # For backward compatibility
|
defineSymbol(conf.symbols, "ecmascript") # For backward compatibility
|
||||||
setPipeLinePass(graph, JSgenPass)
|
semanticPasses(graph)
|
||||||
compilePipelineProject(graph)
|
registerPass(graph, JSgenPass)
|
||||||
|
compileProject(graph)
|
||||||
if optGenScript in conf.globalOptions:
|
if optGenScript in conf.globalOptions:
|
||||||
writeDepsFile(graph)
|
writeDepsFile(graph)
|
||||||
|
|
||||||
proc commandInteractive(graph: ModuleGraph) =
|
proc interactivePasses(graph: ModuleGraph) =
|
||||||
graph.config.setErrorMaxHighMaybe
|
|
||||||
initDefines(graph.config.symbols)
|
initDefines(graph.config.symbols)
|
||||||
defineSymbol(graph.config.symbols, "nimscript")
|
defineSymbol(graph.config.symbols, "nimscript")
|
||||||
# note: seems redundant with -d:nimHasLibFFI
|
# note: seems redundant with -d:nimHasLibFFI
|
||||||
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
||||||
setPipeLinePass(graph, InterpreterPass)
|
registerPass(graph, verbosePass)
|
||||||
compilePipelineSystemModule(graph)
|
registerPass(graph, semPass)
|
||||||
|
registerPass(graph, evalPass)
|
||||||
|
|
||||||
|
proc commandInteractive(graph: ModuleGraph) =
|
||||||
|
graph.config.setErrorMaxHighMaybe
|
||||||
|
interactivePasses(graph)
|
||||||
|
compileSystemModule(graph)
|
||||||
if graph.config.commandArgs.len > 0:
|
if graph.config.commandArgs.len > 0:
|
||||||
discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
|
discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
|
||||||
else:
|
else:
|
||||||
var m = graph.makeStdinModule()
|
var m = graph.makeStdinModule()
|
||||||
incl(m.flags, sfMainModule)
|
incl(m.flags, sfMainModule)
|
||||||
var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||||
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
|
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
|
||||||
discard processPipelineModule(graph, m, idgen, s)
|
processModule(graph, m, idgen, s)
|
||||||
|
|
||||||
proc commandScan(cache: IdentCache, config: ConfigRef) =
|
proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||||
var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
|
var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
|
||||||
|
|
@ -241,6 +209,8 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
let conf = graph.config
|
let conf = graph.config
|
||||||
let cache = graph.cache
|
let cache = graph.cache
|
||||||
|
|
||||||
|
# In "nim serve" scenario, each command must reset the registered passes
|
||||||
|
clearPasses(graph)
|
||||||
conf.lastCmdTime = epochTime()
|
conf.lastCmdTime = epochTime()
|
||||||
conf.searchPaths.add(conf.libpath)
|
conf.searchPaths.add(conf.libpath)
|
||||||
|
|
||||||
|
|
@ -296,8 +266,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
|
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
|
||||||
else: conf.projectPath
|
else: conf.projectPath
|
||||||
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
|
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
|
||||||
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}:
|
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir
|
||||||
ret = ret / htmldocsDir
|
|
||||||
conf.outDir = ret
|
conf.outDir = ret
|
||||||
|
|
||||||
## process all commands
|
## process all commands
|
||||||
|
|
@ -312,9 +281,10 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
else:
|
else:
|
||||||
rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
|
rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
|
||||||
of cmdDoc0: docLikeCmd commandDoc(cache, conf)
|
of cmdDoc0: docLikeCmd commandDoc(cache, conf)
|
||||||
of cmdDoc:
|
of cmdDoc2:
|
||||||
docLikeCmd():
|
docLikeCmd():
|
||||||
conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # issue #13218
|
conf.setNoteDefaults(warnLockLevel, false) # issue #13218
|
||||||
|
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218
|
||||||
# because currently generates lots of false positives due to conflation
|
# because currently generates lots of false positives due to conflation
|
||||||
# of labels links in doc comments, e.g. for random.rand:
|
# of labels links in doc comments, e.g. for random.rand:
|
||||||
# ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
# ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
|
||||||
|
|
@ -322,33 +292,32 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
commandDoc2(graph, HtmlExt)
|
commandDoc2(graph, HtmlExt)
|
||||||
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
|
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
|
||||||
commandBuildIndex(conf, $conf.outDir)
|
commandBuildIndex(conf, $conf.outDir)
|
||||||
of cmdRst2html, cmdMd2html:
|
of cmdRst2html:
|
||||||
# XXX: why are warnings disabled by default for rst2html and rst2tex?
|
# XXX: why are warnings disabled by default for rst2html and rst2tex?
|
||||||
for warn in rstWarnings:
|
for warn in [warnUnknownSubstitutionX, warnLanguageXNotSupported,
|
||||||
|
warnFieldXNotSupported, warnRstStyle]:
|
||||||
conf.setNoteDefaults(warn, true)
|
conf.setNoteDefaults(warn, true)
|
||||||
conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # similar to issue #13218
|
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218
|
||||||
when defined(leanCompiler):
|
when defined(leanCompiler):
|
||||||
conf.quitOrRaise "compiler wasn't built with documentation generator"
|
conf.quitOrRaise "compiler wasn't built with documentation generator"
|
||||||
else:
|
else:
|
||||||
loadConfigs(DocConfig, cache, conf, graph.idgen)
|
loadConfigs(DocConfig, cache, conf, graph.idgen)
|
||||||
commandRst2Html(cache, conf, preferMarkdown = (conf.cmd == cmdMd2html))
|
commandRst2Html(cache, conf)
|
||||||
of cmdRst2tex, cmdMd2tex, cmdDoc2tex:
|
of cmdRst2tex, cmdDoc2tex:
|
||||||
for warn in rstWarnings:
|
for warn in [warnRedefinitionOfLabel, warnUnknownSubstitutionX,
|
||||||
|
warnLanguageXNotSupported,
|
||||||
|
warnFieldXNotSupported, warnRstStyle]:
|
||||||
conf.setNoteDefaults(warn, true)
|
conf.setNoteDefaults(warn, true)
|
||||||
when defined(leanCompiler):
|
when defined(leanCompiler):
|
||||||
conf.quitOrRaise "compiler wasn't built with documentation generator"
|
conf.quitOrRaise "compiler wasn't built with documentation generator"
|
||||||
else:
|
else:
|
||||||
if conf.cmd in {cmdRst2tex, cmdMd2tex}:
|
if conf.cmd == cmdRst2tex:
|
||||||
loadConfigs(DocTexConfig, cache, conf, graph.idgen)
|
loadConfigs(DocTexConfig, cache, conf, graph.idgen)
|
||||||
commandRst2TeX(cache, conf, preferMarkdown = (conf.cmd == cmdMd2tex))
|
commandRst2TeX(cache, conf)
|
||||||
else:
|
else:
|
||||||
docLikeCmd commandDoc2(graph, TexExt)
|
docLikeCmd commandDoc2(graph, TexExt)
|
||||||
of cmdJsondoc0: docLikeCmd commandJson(cache, conf)
|
of cmdJsondoc0: docLikeCmd commandJson(cache, conf)
|
||||||
of cmdJsondoc:
|
of cmdJsondoc: docLikeCmd commandDoc2(graph, JsonExt)
|
||||||
docLikeCmd():
|
|
||||||
commandDoc2(graph, JsonExt)
|
|
||||||
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
|
|
||||||
commandBuildIndexJson(conf, $conf.outDir)
|
|
||||||
of cmdCtags: docLikeCmd commandTags(cache, conf)
|
of cmdCtags: docLikeCmd commandTags(cache, conf)
|
||||||
of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
|
of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
|
||||||
of cmdGendepend: commandGenDepend(graph)
|
of cmdGendepend: commandGenDepend(graph)
|
||||||
|
|
@ -396,8 +365,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
|
msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
|
||||||
|
|
||||||
for it in conf.searchPaths: msgWriteln(conf, it.string)
|
for it in conf.searchPaths: msgWriteln(conf, it.string)
|
||||||
of cmdCheck:
|
of cmdCheck: commandCheck(graph)
|
||||||
commandCheck(graph)
|
|
||||||
of cmdParse:
|
of cmdParse:
|
||||||
wantMainModule(conf)
|
wantMainModule(conf)
|
||||||
discard parseFile(conf.projectMainIdx, cache, conf)
|
discard parseFile(conf.projectMainIdx, cache, conf)
|
||||||
|
|
@ -419,9 +387,37 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||||
rawMessage(conf, errGenerated, "invalid command: " & conf.command)
|
rawMessage(conf, errGenerated, "invalid command: " & conf.command)
|
||||||
|
|
||||||
if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
|
if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
|
||||||
|
# D20210419T170230:here
|
||||||
|
let mem =
|
||||||
|
when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
|
||||||
|
else: formatSize(getTotalMem()) & " totmem"
|
||||||
|
let loc = $conf.linesCompiled
|
||||||
|
let build = if isDefined(conf, "danger"): "Dangerous Release build"
|
||||||
|
elif isDefined(conf, "release"): "Release build"
|
||||||
|
else: "***SLOW, DEBUG BUILD***; -d:release makes code run faster."
|
||||||
|
let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
|
||||||
|
let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName
|
||||||
|
# xxx honor conf.filenameOption more accurately
|
||||||
|
var output: string
|
||||||
|
if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript:
|
||||||
|
output = $conf.jsonBuildFile
|
||||||
|
elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
|
||||||
|
# for some cmd we expect a valid absOutFile
|
||||||
|
output = "unknownOutput"
|
||||||
|
else:
|
||||||
|
output = $conf.absOutFile
|
||||||
|
if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename
|
||||||
|
# xxx honor filenameOption more accurately
|
||||||
if optProfileVM in conf.globalOptions:
|
if optProfileVM in conf.globalOptions:
|
||||||
echo conf.dump(conf.vmProfileData)
|
echo conf.dump(conf.vmProfileData)
|
||||||
genSuccessX(conf)
|
rawMessage(conf, hintSuccessX, [
|
||||||
|
"loc", loc,
|
||||||
|
"sec", sec,
|
||||||
|
"mem", mem,
|
||||||
|
"project", project,
|
||||||
|
"output", output,
|
||||||
|
])
|
||||||
|
rawMessage(conf, hintBuildMode, build)
|
||||||
|
|
||||||
when PrintRopeCacheStats:
|
when PrintRopeCacheStats:
|
||||||
echo "rope cache stats: "
|
echo "rope cache stats: "
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,9 @@
|
||||||
## or stored in a rod-file.
|
## or stored in a rod-file.
|
||||||
|
|
||||||
import intsets, tables, hashes, md5
|
import intsets, tables, hashes, md5
|
||||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
|
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils
|
||||||
import ic / [packed_ast, ic]
|
import ic / [packed_ast, ic]
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
SigHash* = distinct MD5Digest
|
SigHash* = distinct MD5Digest
|
||||||
|
|
||||||
|
|
@ -53,22 +50,6 @@ type
|
||||||
concreteTypes*: seq[FullId]
|
concreteTypes*: seq[FullId]
|
||||||
inst*: PInstantiation
|
inst*: PInstantiation
|
||||||
|
|
||||||
SymInfoPair* = object
|
|
||||||
sym*: PSym
|
|
||||||
info*: TLineInfo
|
|
||||||
|
|
||||||
PipelinePass* = enum
|
|
||||||
NonePass
|
|
||||||
SemPass
|
|
||||||
JSgenPass
|
|
||||||
CgenPass
|
|
||||||
EvalPass
|
|
||||||
InterpreterPass
|
|
||||||
GenDependPass
|
|
||||||
Docgen2TexPass
|
|
||||||
Docgen2JsonPass
|
|
||||||
Docgen2Pass
|
|
||||||
|
|
||||||
ModuleGraph* {.acyclic.} = ref object
|
ModuleGraph* {.acyclic.} = ref object
|
||||||
ifaces*: seq[Iface] ## indexed by int32 fileIdx
|
ifaces*: seq[Iface] ## indexed by int32 fileIdx
|
||||||
packed*: PackedModuleGraph
|
packed*: PackedModuleGraph
|
||||||
|
|
@ -76,13 +57,14 @@ type
|
||||||
|
|
||||||
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
|
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
|
||||||
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
|
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
|
||||||
attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
|
attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
|
||||||
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
||||||
enumToStringProcs*: Table[ItemId, LazySym]
|
enumToStringProcs*: Table[ItemId, LazySym]
|
||||||
emittedTypeInfo*: Table[string, FileIndex]
|
emittedTypeInfo*: Table[string, FileIndex]
|
||||||
|
|
||||||
startupPackedConfig*: PackedConfig
|
startupPackedConfig*: PackedConfig
|
||||||
packageSyms*: TStrTable
|
packageSyms*: TStrTable
|
||||||
|
modulesPerPackage*: Table[ItemId, TStrTable]
|
||||||
deps*: IntSet # the dependency graph or potentially its transitive closure.
|
deps*: IntSet # the dependency graph or potentially its transitive closure.
|
||||||
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
|
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
|
||||||
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
||||||
|
|
@ -99,8 +81,6 @@ type
|
||||||
doStopCompile*: proc(): bool {.closure.}
|
doStopCompile*: proc(): bool {.closure.}
|
||||||
usageSym*: PSym # for nimsuggest
|
usageSym*: PSym # for nimsuggest
|
||||||
owners*: seq[PSym]
|
owners*: seq[PSym]
|
||||||
suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
|
|
||||||
suggestErrors*: Table[FileIndex, seq[Suggest]]
|
|
||||||
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
|
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
|
||||||
systemModule*: PSym
|
systemModule*: PSym
|
||||||
sysTypes*: array[TTypeKind, PType]
|
sysTypes*: array[TTypeKind, PType]
|
||||||
|
|
@ -116,7 +96,6 @@ type
|
||||||
cacheCounters*: Table[string, BiggestInt] # IC: implemented
|
cacheCounters*: Table[string, BiggestInt] # IC: implemented
|
||||||
cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
|
cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
|
||||||
passes*: seq[TPass]
|
passes*: seq[TPass]
|
||||||
pipelinePass*: PipelinePass
|
|
||||||
onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||||
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||||
onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||||
|
|
@ -298,13 +277,6 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
|
||||||
t.inst = result
|
t.inst = result
|
||||||
assert result != nil
|
assert result != nil
|
||||||
|
|
||||||
proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym =
|
|
||||||
result = t.sym
|
|
||||||
if result == nil:
|
|
||||||
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
|
|
||||||
t.sym = result
|
|
||||||
assert result != nil
|
|
||||||
|
|
||||||
iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
|
iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
|
||||||
if g.typeInstCache.contains(s.itemId):
|
if g.typeInstCache.contains(s.itemId):
|
||||||
let x = addr(g.typeInstCache[s.itemId])
|
let x = addr(g.typeInstCache[s.itemId])
|
||||||
|
|
@ -320,25 +292,22 @@ iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
|
||||||
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
|
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
|
||||||
## returns the requested attached operation for type `t`. Can return nil
|
## returns the requested attached operation for type `t`. Can return nil
|
||||||
## if no such operation exists.
|
## if no such operation exists.
|
||||||
if g.attachedOps[op].contains(t.itemId):
|
result = g.attachedOps[op].getOrDefault(t.itemId)
|
||||||
result = resolveAttachedOp(g, g.attachedOps[op][t.itemId])
|
|
||||||
else:
|
|
||||||
result = nil
|
|
||||||
|
|
||||||
proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||||
## we also need to record this to the packed module.
|
## we also need to record this to the packed module.
|
||||||
g.attachedOps[op][t.itemId] = LazySym(sym: value)
|
g.attachedOps[op][t.itemId] = value
|
||||||
|
|
||||||
proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||||
## we also need to record this to the packed module.
|
## we also need to record this to the packed module.
|
||||||
g.attachedOps[op][t.itemId] = LazySym(sym: value)
|
g.attachedOps[op][t.itemId] = value
|
||||||
|
# XXX Also add to the packed module!
|
||||||
|
|
||||||
proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||||
if g.config.symbolFiles != disabledSf:
|
if g.config.symbolFiles != disabledSf:
|
||||||
assert module < g.encoders.len
|
assert module < g.encoders.len
|
||||||
assert isActive(g.encoders[module])
|
assert isActive(g.encoders[module])
|
||||||
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
|
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
|
||||||
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
|
|
||||||
|
|
||||||
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
|
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
|
||||||
result = resolveSym(g, g.enumToStringProcs[t.itemId])
|
result = resolveSym(g, g.enumToStringProcs[t.itemId])
|
||||||
|
|
@ -399,9 +368,20 @@ template getPContext(): untyped =
|
||||||
when c is PContext: c
|
when c is PContext: c
|
||||||
else: c.c
|
else: c.c
|
||||||
|
|
||||||
when defined(nimsuggest):
|
when defined(nimfind):
|
||||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
template onUse*(info: TLineInfo; s: PSym) =
|
||||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
let c = getPContext()
|
||||||
|
if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info)
|
||||||
|
|
||||||
|
template onDef*(info: TLineInfo; s: PSym) =
|
||||||
|
let c = getPContext()
|
||||||
|
if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info)
|
||||||
|
|
||||||
|
template onDefResolveForward*(info: TLineInfo; s: PSym) =
|
||||||
|
let c = getPContext()
|
||||||
|
if c.graph.onDefinitionResolveForward != nil:
|
||||||
|
c.graph.onDefinitionResolveForward(c.graph, s, info)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||||
template onDef*(info: TLineInfo; s: PSym) = discard
|
template onDef*(info: TLineInfo; s: PSym) = discard
|
||||||
|
|
@ -452,7 +432,8 @@ proc initOperators*(g: ModuleGraph): Operators =
|
||||||
result.opNot = createMagic(g, "not", mNot)
|
result.opNot = createMagic(g, "not", mNot)
|
||||||
result.opContains = createMagic(g, "contains", mInSet)
|
result.opContains = createMagic(g, "contains", mInSet)
|
||||||
|
|
||||||
proc initModuleGraphFields(result: ModuleGraph) =
|
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||||
|
result = ModuleGraph()
|
||||||
# A module ID of -1 means that the symbol is not attached to a module at all,
|
# A module ID of -1 means that the symbol is not attached to a module at all,
|
||||||
# but to the module graph:
|
# but to the module graph:
|
||||||
result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
|
result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
|
||||||
|
|
@ -462,9 +443,9 @@ proc initModuleGraphFields(result: ModuleGraph) =
|
||||||
result.ifaces = @[]
|
result.ifaces = @[]
|
||||||
result.importStack = @[]
|
result.importStack = @[]
|
||||||
result.inclToMod = initTable[FileIndex, FileIndex]()
|
result.inclToMod = initTable[FileIndex, FileIndex]()
|
||||||
|
result.config = config
|
||||||
|
result.cache = cache
|
||||||
result.owners = @[]
|
result.owners = @[]
|
||||||
result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]()
|
|
||||||
result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
|
|
||||||
result.methods = @[]
|
result.methods = @[]
|
||||||
initStrTable(result.compilerprocs)
|
initStrTable(result.compilerprocs)
|
||||||
initStrTable(result.exposed)
|
initStrTable(result.exposed)
|
||||||
|
|
@ -478,12 +459,6 @@ proc initModuleGraphFields(result: ModuleGraph) =
|
||||||
result.operators = initOperators(result)
|
result.operators = initOperators(result)
|
||||||
result.emittedTypeInfo = initTable[string, FileIndex]()
|
result.emittedTypeInfo = initTable[string, FileIndex]()
|
||||||
|
|
||||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
|
||||||
result = ModuleGraph()
|
|
||||||
result.config = config
|
|
||||||
result.cache = cache
|
|
||||||
initModuleGraphFields(result)
|
|
||||||
|
|
||||||
proc resetAllModules*(g: ModuleGraph) =
|
proc resetAllModules*(g: ModuleGraph) =
|
||||||
initStrTable(g.packageSyms)
|
initStrTable(g.packageSyms)
|
||||||
g.deps = initIntSet()
|
g.deps = initIntSet()
|
||||||
|
|
@ -495,7 +470,6 @@ proc resetAllModules*(g: ModuleGraph) =
|
||||||
g.methods = @[]
|
g.methods = @[]
|
||||||
initStrTable(g.compilerprocs)
|
initStrTable(g.compilerprocs)
|
||||||
initStrTable(g.exposed)
|
initStrTable(g.exposed)
|
||||||
initModuleGraphFields(g)
|
|
||||||
|
|
||||||
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
|
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
if fileIdx.int32 >= 0:
|
if fileIdx.int32 >= 0:
|
||||||
|
|
@ -574,19 +548,7 @@ proc transitiveClosure(g: var IntSet; n: int) =
|
||||||
|
|
||||||
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||||
let m = g.getModule fileIdx
|
let m = g.getModule fileIdx
|
||||||
if m != nil:
|
if m != nil: incl m.flags, sfDirty
|
||||||
g.suggestSymbols.del(fileIdx)
|
|
||||||
g.suggestErrors.del(fileIdx)
|
|
||||||
incl m.flags, sfDirty
|
|
||||||
|
|
||||||
proc unmarkAllDirty*(g: ModuleGraph) =
|
|
||||||
for i in 0i32..<g.ifaces.len.int32:
|
|
||||||
let m = g.ifaces[i].module
|
|
||||||
if m != nil:
|
|
||||||
m.flags.excl sfDirty
|
|
||||||
|
|
||||||
proc isDirty*(g: ModuleGraph; m: PSym): bool =
|
|
||||||
result = g.suggestMode and sfDirty in m.flags
|
|
||||||
|
|
||||||
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||||
# we need to mark its dependent modules D as dirty right away because after
|
# we need to mark its dependent modules D as dirty right away because after
|
||||||
|
|
@ -596,28 +558,14 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||||
g.invalidTransitiveClosure = false
|
g.invalidTransitiveClosure = false
|
||||||
transitiveClosure(g.deps, g.ifaces.len)
|
transitiveClosure(g.deps, g.ifaces.len)
|
||||||
|
|
||||||
# every module that *depends* on this file is also dirty:
|
|
||||||
for i in 0i32..<g.ifaces.len.int32:
|
|
||||||
if g.deps.contains(i.dependsOn(fileIdx.int)):
|
|
||||||
g.markDirty(FileIndex(i))
|
|
||||||
|
|
||||||
proc needsCompilation*(g: ModuleGraph): bool =
|
|
||||||
# every module that *depends* on this file is also dirty:
|
# every module that *depends* on this file is also dirty:
|
||||||
for i in 0i32..<g.ifaces.len.int32:
|
for i in 0i32..<g.ifaces.len.int32:
|
||||||
let m = g.ifaces[i].module
|
let m = g.ifaces[i].module
|
||||||
if m != nil:
|
if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
|
||||||
if sfDirty in m.flags:
|
incl m.flags, sfDirty
|
||||||
return true
|
|
||||||
|
|
||||||
proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
|
proc isDirty*(g: ModuleGraph; m: PSym): bool =
|
||||||
let module = g.getModule(fileIdx)
|
result = g.suggestMode and sfDirty in m.flags
|
||||||
if module != nil and g.isDirty(module):
|
|
||||||
return true
|
|
||||||
|
|
||||||
for i in 0i32..<g.ifaces.len.int32:
|
|
||||||
let m = g.ifaces[i].module
|
|
||||||
if m != nil and g.isDirty(m) and g.deps.contains(fileIdx.int32.dependsOn(i)):
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
|
proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
|
||||||
result = s.ast[bodyPos]
|
result = s.ast[bodyPos]
|
||||||
|
|
@ -634,46 +582,3 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||||
|
|
||||||
proc configComplete*(g: ModuleGraph) =
|
proc configComplete*(g: ModuleGraph) =
|
||||||
rememberStartupConfig(g.startupPackedConfig, g.config)
|
rememberStartupConfig(g.startupPackedConfig, g.config)
|
||||||
|
|
||||||
from std/strutils import repeat, `%`
|
|
||||||
|
|
||||||
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
|
|
||||||
let conf = graph.config
|
|
||||||
let isNimscript = conf.isDefined("nimscript")
|
|
||||||
if (not isNimscript) or hintProcessing in conf.cmdlineNotes:
|
|
||||||
let path = toFilenameOption(conf, fileIdx, conf.filenameOption)
|
|
||||||
let indent = ">".repeat(graph.importStack.len)
|
|
||||||
let fromModule2 = if fromModule != nil: $fromModule.name.s else: "(toplevel)"
|
|
||||||
let mode = if isNimscript: "(nims) " else: ""
|
|
||||||
rawMessage(conf, hintProcessing, "$#$# $#: $#: $#" % [mode, indent, fromModule2, moduleStatus, path])
|
|
||||||
|
|
||||||
proc getPackage*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
|
||||||
## Returns a package symbol for yet to be defined module for fileIdx.
|
|
||||||
## The package symbol is added to the graph if it doesn't exist.
|
|
||||||
let pkgSym = getPackage(graph.config, graph.cache, fileIdx)
|
|
||||||
# check if the package is already in the graph
|
|
||||||
result = graph.packageSyms.strTableGet(pkgSym.name)
|
|
||||||
if result == nil:
|
|
||||||
# the package isn't in the graph, so create and add it
|
|
||||||
result = pkgSym
|
|
||||||
graph.packageSyms.strTableAdd(pkgSym)
|
|
||||||
|
|
||||||
func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
|
|
||||||
## Check if symbol belongs to the 'stdlib' package.
|
|
||||||
sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
|
|
||||||
|
|
||||||
proc `==`*(a, b: SymInfoPair): bool =
|
|
||||||
result = a.sym == b.sym and a.info.exactEquals(b.info)
|
|
||||||
|
|
||||||
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): seq[SymInfoPair] =
|
|
||||||
result = graph.suggestSymbols.getOrDefault(fileIdx, @[])
|
|
||||||
|
|
||||||
iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
|
|
||||||
for xs in g.suggestSymbols.values:
|
|
||||||
for x in xs:
|
|
||||||
yield x
|
|
||||||
|
|
||||||
iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
|
|
||||||
for xs in g.suggestErrors.values:
|
|
||||||
for x in xs:
|
|
||||||
yield x
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,100 @@
|
||||||
import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
|
import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
|
||||||
pathutils
|
pathutils
|
||||||
|
|
||||||
|
when false:
|
||||||
|
const
|
||||||
|
considerParentDirs = not defined(noParentProjects)
|
||||||
|
considerNimbleDirs = not defined(noNimbleDirs)
|
||||||
|
|
||||||
|
proc findInNimbleDir(pkg, subdir, dir: string): string =
|
||||||
|
var best = ""
|
||||||
|
var bestv = ""
|
||||||
|
for k, p in os.walkDir(dir, relative=true):
|
||||||
|
if k == pcDir and p.len > pkg.len+1 and
|
||||||
|
p[pkg.len] == '-' and p.startsWith(pkg):
|
||||||
|
let (_, a) = getPathVersion(p)
|
||||||
|
if bestv.len == 0 or bestv < a:
|
||||||
|
bestv = a
|
||||||
|
best = dir / p
|
||||||
|
|
||||||
|
if best.len > 0:
|
||||||
|
var f: File
|
||||||
|
if open(f, best / changeFileExt(pkg, ".nimble-link")):
|
||||||
|
# the second line contains what we're interested in, see:
|
||||||
|
# https://github.com/nim-lang/nimble#nimble-link
|
||||||
|
var override = ""
|
||||||
|
discard readLine(f, override)
|
||||||
|
discard readLine(f, override)
|
||||||
|
close(f)
|
||||||
|
if not override.isAbsolute():
|
||||||
|
best = best / override
|
||||||
|
else:
|
||||||
|
best = override
|
||||||
|
let f = if subdir.len == 0: pkg else: subdir
|
||||||
|
let res = addFileExt(best / f, "nim")
|
||||||
|
if best.len > 0 and fileExists(res):
|
||||||
|
result = res
|
||||||
|
|
||||||
|
when false:
|
||||||
|
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
|
||||||
|
template attempt(a) =
|
||||||
|
let x = addFileExt(a, "nim")
|
||||||
|
if fileExists(x): return x
|
||||||
|
|
||||||
|
case pkg
|
||||||
|
of "stdlib":
|
||||||
|
if subdir.len == 0:
|
||||||
|
return options.libpath
|
||||||
|
else:
|
||||||
|
for candidate in stdlibDirs:
|
||||||
|
attempt(options.libpath / candidate / subdir)
|
||||||
|
of "root":
|
||||||
|
let root = project.splitFile.dir
|
||||||
|
if subdir.len == 0:
|
||||||
|
return root
|
||||||
|
else:
|
||||||
|
attempt(root / subdir)
|
||||||
|
else:
|
||||||
|
when considerParentDirs:
|
||||||
|
var p = parentDir(source.splitFile.dir)
|
||||||
|
# support 'import $karax':
|
||||||
|
let f = if subdir.len == 0: pkg else: subdir
|
||||||
|
|
||||||
|
while p.len > 0:
|
||||||
|
let dir = p / pkg
|
||||||
|
if dirExists(dir):
|
||||||
|
attempt(dir / f)
|
||||||
|
# 2nd attempt: try to use 'karax/karax'
|
||||||
|
attempt(dir / pkg / f)
|
||||||
|
# 3rd attempt: try to use 'karax/src/karax'
|
||||||
|
attempt(dir / "src" / f)
|
||||||
|
attempt(dir / "src" / pkg / f)
|
||||||
|
p = parentDir(p)
|
||||||
|
|
||||||
|
when considerNimbleDirs:
|
||||||
|
if not options.gNoNimblePath:
|
||||||
|
var nimbleDir = getEnv("NIMBLE_DIR")
|
||||||
|
if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
|
||||||
|
result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
|
||||||
|
if result.len > 0: return result
|
||||||
|
when not defined(windows):
|
||||||
|
result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
|
||||||
|
if result.len > 0: return result
|
||||||
|
|
||||||
|
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
|
||||||
|
resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
|
||||||
|
|
||||||
|
proc lookupPackage(pkg, subdir: PNode): string =
|
||||||
|
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
|
||||||
|
case pkg.kind
|
||||||
|
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||||
|
result = scriptableImport(pkg.strVal, sub, pkg.info)
|
||||||
|
of nkIdent:
|
||||||
|
result = scriptableImport(pkg.ident.s, sub, pkg.info)
|
||||||
|
else:
|
||||||
|
localError(pkg.info, "package name must be an identifier or string literal")
|
||||||
|
result = ""
|
||||||
|
|
||||||
proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||||
# This returns a short relative module name without the nim extension
|
# This returns a short relative module name without the nim extension
|
||||||
# e.g. like "system", "importer" or "somepath/module"
|
# e.g. like "system", "importer" or "somepath/module"
|
||||||
|
|
@ -69,18 +163,3 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
|
||||||
result = InvalidFileIdx
|
result = InvalidFileIdx
|
||||||
else:
|
else:
|
||||||
result = fileInfoIdx(conf, fullPath)
|
result = fileInfoIdx(conf, fullPath)
|
||||||
|
|
||||||
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
|
||||||
## Mangle a relative module path to avoid path and symbol collisions.
|
|
||||||
##
|
|
||||||
## Used by backends that need to generate intermediary files from Nim modules.
|
|
||||||
## This is needed because the compiler uses a flat cache file hierarchy.
|
|
||||||
##
|
|
||||||
## Example:
|
|
||||||
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
|
|
||||||
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
|
|
||||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
|
||||||
|
|
||||||
proc demangleModuleName*(path: string): string =
|
|
||||||
## Demangle a relative module path.
|
|
||||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
|
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,11 @@
|
||||||
## Implements the module handling, including the caching of modules.
|
## Implements the module handling, including the caching of modules.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, magicsys, msgs, options,
|
ast, astalgo, magicsys, msgs, options,
|
||||||
idents, lexer, syntaxes, modulegraphs,
|
idents, lexer, passes, syntaxes, llstream, modulegraphs,
|
||||||
lineinfos, pathutils
|
lineinfos, pathutils, tables
|
||||||
|
|
||||||
|
import ic / replayer
|
||||||
|
|
||||||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||||
magicsys.resetSysTypes(g)
|
magicsys.resetSysTypes(g)
|
||||||
|
|
@ -20,12 +22,57 @@ proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||||
template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
|
template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
|
||||||
getIdent(graph.cache, splitFile(filename).name)
|
getIdent(graph.cache, splitFile(filename).name)
|
||||||
|
|
||||||
proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
template packageId(): untyped {.dirty.} = ItemId(module: PackageModuleId, item: int32(fileIdx))
|
||||||
|
|
||||||
|
proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
|
## returns package symbol (skPackage) for yet to be defined module for fileIdx
|
||||||
|
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||||
|
let name = getModuleIdent(graph, filename)
|
||||||
|
let info = newLineInfo(fileIdx, 1, 1)
|
||||||
|
let
|
||||||
|
pck = getPackageName(graph.config, filename.string)
|
||||||
|
pck2 = if pck.len > 0: pck else: "unknown"
|
||||||
|
pack = getIdent(graph.cache, pck2)
|
||||||
|
result = graph.packageSyms.strTableGet(pack)
|
||||||
|
if result == nil:
|
||||||
|
result = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info)
|
||||||
|
#initStrTable(packSym.tab)
|
||||||
|
graph.packageSyms.strTableAdd(result)
|
||||||
|
else:
|
||||||
|
let modules = graph.modulesPerPackage.getOrDefault(result.itemId)
|
||||||
|
let existing = if modules.data.len > 0: strTableGet(modules, name) else: nil
|
||||||
|
if existing != nil and existing.info.fileIndex != info.fileIndex:
|
||||||
|
when false:
|
||||||
|
# we used to produce an error:
|
||||||
|
localError(graph.config, info,
|
||||||
|
"module names need to be unique per Nimble package; module clashes with " &
|
||||||
|
toFullPath(graph.config, existing.info.fileIndex))
|
||||||
|
else:
|
||||||
|
# but starting with version 0.20 we now produce a fake Nimble package instead
|
||||||
|
# to resolve the conflicts:
|
||||||
|
let pck3 = fakePackageName(graph.config, filename)
|
||||||
|
# this makes the new `result`'s owner be the original `result`
|
||||||
|
result = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), result, info)
|
||||||
|
#initStrTable(packSym.tab)
|
||||||
|
graph.packageSyms.strTableAdd(result)
|
||||||
|
|
||||||
|
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||||
let packSym = getPackage(graph, fileIdx)
|
let packSym = getPackage(graph, fileIdx)
|
||||||
result.owner = packSym
|
result.owner = packSym
|
||||||
result.position = int fileIdx
|
result.position = int fileIdx
|
||||||
|
|
||||||
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
#initStrTable(result.tab(graph))
|
||||||
|
when false:
|
||||||
|
strTableAdd(result.tab, result) # a module knows itself
|
||||||
|
# This is now implemented via
|
||||||
|
# c.moduleScope.addSym(module) # a module knows itself
|
||||||
|
# in sem.nim, around line 527
|
||||||
|
|
||||||
|
if graph.modulesPerPackage.getOrDefault(packSym.itemId).data.len == 0:
|
||||||
|
graph.modulesPerPackage[packSym.itemId] = newStrTable()
|
||||||
|
graph.modulesPerPackage[packSym.itemId].strTableAdd(result)
|
||||||
|
|
||||||
|
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||||
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
||||||
# mechanism, which we do in order to assign each module a persistent ID.
|
# mechanism, which we do in order to assign each module a persistent ID.
|
||||||
|
|
@ -33,21 +80,103 @@ proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||||
name: getModuleIdent(graph, filename),
|
name: getModuleIdent(graph, filename),
|
||||||
info: newLineInfo(fileIdx, 1, 1))
|
info: newLineInfo(fileIdx, 1, 1))
|
||||||
if not isNimIdentifier(result.name.s):
|
if not isNimIdentifier(result.name.s):
|
||||||
rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s &
|
rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
|
||||||
"'; a module name must be a valid Nim identifier.")
|
|
||||||
partialInitModule(result, graph, fileIdx, filename)
|
partialInitModule(result, graph, fileIdx, filename)
|
||||||
graph.registerModule(result)
|
graph.registerModule(result)
|
||||||
|
|
||||||
|
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
|
||||||
|
var flags = flags
|
||||||
|
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
|
||||||
|
result = graph.getModule(fileIdx)
|
||||||
|
|
||||||
|
template processModuleAux =
|
||||||
|
var s: PLLStream
|
||||||
|
if sfMainModule in flags:
|
||||||
|
if graph.config.projectIsStdin: s = stdin.llStreamOpen
|
||||||
|
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
|
||||||
|
discard processModule(graph, result, idGeneratorFromModule(result), s)
|
||||||
|
if result == nil:
|
||||||
|
var cachedModules: seq[FileIndex]
|
||||||
|
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||||
|
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||||
|
if result == nil:
|
||||||
|
result = newModule(graph, fileIdx)
|
||||||
|
result.flags.incl flags
|
||||||
|
registerModule(graph, result)
|
||||||
|
processModuleAux()
|
||||||
|
else:
|
||||||
|
if sfSystemModule in flags:
|
||||||
|
graph.systemModule = result
|
||||||
|
partialInitModule(result, graph, fileIdx, filename)
|
||||||
|
for m in cachedModules:
|
||||||
|
registerModuleById(graph, m)
|
||||||
|
replayStateChanges(graph.packed[m.int].module, graph)
|
||||||
|
replayGenericCacheInformation(graph, m.int)
|
||||||
|
elif graph.isDirty(result):
|
||||||
|
result.flags.excl sfDirty
|
||||||
|
# reset module fields:
|
||||||
|
initStrTables(graph, result)
|
||||||
|
result.ast = nil
|
||||||
|
processModuleAux()
|
||||||
|
graph.markClientsDirty(fileIdx)
|
||||||
|
|
||||||
|
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
|
||||||
|
# this is called by the semantic checking phase
|
||||||
|
assert graph.config != nil
|
||||||
|
result = compileModule(graph, fileIdx, {})
|
||||||
|
graph.addDep(s, fileIdx)
|
||||||
|
# keep track of import relationships
|
||||||
|
if graph.config.hcrOn:
|
||||||
|
graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
|
||||||
|
#if sfSystemModule in result.flags:
|
||||||
|
# localError(result.info, errAttemptToRedefine, result.name.s)
|
||||||
|
# restore the notes for outer module:
|
||||||
|
graph.config.notes =
|
||||||
|
if s.getnimblePkgId == graph.config.mainPackageId or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
|
||||||
|
else: graph.config.foreignPackageNotes
|
||||||
|
|
||||||
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
|
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
|
||||||
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
|
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
|
||||||
graph.addDep(s, fileIdx)
|
graph.addDep(s, fileIdx)
|
||||||
graph.addIncludeDep(s.position.FileIndex, fileIdx)
|
graph.addIncludeDep(s.position.FileIndex, fileIdx)
|
||||||
|
|
||||||
|
proc connectCallbacks*(graph: ModuleGraph) =
|
||||||
|
graph.includeFileCallback = includeModule
|
||||||
|
graph.importModuleCallback = importModule
|
||||||
|
|
||||||
|
proc compileSystemModule*(graph: ModuleGraph) =
|
||||||
|
if graph.systemModule == nil:
|
||||||
|
connectCallbacks(graph)
|
||||||
|
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
|
||||||
|
graph.config.libpath / RelativeFile"system.nim")
|
||||||
|
discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
|
||||||
|
|
||||||
proc wantMainModule*(conf: ConfigRef) =
|
proc wantMainModule*(conf: ConfigRef) =
|
||||||
if conf.projectFull.isEmpty:
|
if conf.projectFull.isEmpty:
|
||||||
fatal(conf, gCmdLineInfo, "command expects a filename")
|
fatal(conf, newLineInfo(conf, AbsoluteFile(commandLineDesc), 1, 1), errGenerated,
|
||||||
|
"command expects a filename")
|
||||||
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
|
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
|
||||||
|
|
||||||
|
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
|
||||||
|
connectCallbacks(graph)
|
||||||
|
let conf = graph.config
|
||||||
|
wantMainModule(conf)
|
||||||
|
configComplete(graph)
|
||||||
|
|
||||||
|
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
|
||||||
|
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
|
||||||
|
conf.projectMainIdx2 = projectFile
|
||||||
|
|
||||||
|
let packSym = getPackage(graph, projectFile)
|
||||||
|
graph.config.mainPackageId = packSym.getnimblePkgId
|
||||||
|
graph.importStack.add projectFile
|
||||||
|
|
||||||
|
if projectFile == systemFileIdx:
|
||||||
|
discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
|
||||||
|
else:
|
||||||
|
graph.compileSystemModule()
|
||||||
|
discard graph.compileModule(projectFile, {sfMainModule})
|
||||||
|
|
||||||
proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
|
proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
|
||||||
result = graph.newModule(fileInfoIdx(graph.config, filename))
|
result = graph.newModule(fileInfoIdx(graph.config, filename))
|
||||||
registerModule(graph, result)
|
registerModule(graph, result)
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,10 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[strutils, os, tables, terminal, macros, times],
|
options, strutils, os, tables, ropes, terminal, macros,
|
||||||
std/private/miscdollars,
|
lineinfos, pathutils
|
||||||
options, lineinfos, pathutils
|
import std/private/miscdollars
|
||||||
|
import strutils2
|
||||||
import ropes except `%`
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[syncio, assertions]
|
|
||||||
|
|
||||||
|
|
||||||
type InstantiationInfo* = typeof(instantiationInfo())
|
type InstantiationInfo* = typeof(instantiationInfo())
|
||||||
template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
|
template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
|
||||||
|
|
@ -24,10 +19,6 @@ template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
|
||||||
template toStdOrrKind(stdOrr): untyped =
|
template toStdOrrKind(stdOrr): untyped =
|
||||||
if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
|
if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
|
||||||
|
|
||||||
proc toLowerAscii(a: var string) {.inline.} =
|
|
||||||
for c in mitems(a):
|
|
||||||
if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8)
|
|
||||||
|
|
||||||
proc flushDot*(conf: ConfigRef) =
|
proc flushDot*(conf: ConfigRef) =
|
||||||
## safe to call multiple times
|
## safe to call multiple times
|
||||||
# xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
|
# xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
|
||||||
|
|
@ -37,7 +28,7 @@ proc flushDot*(conf: ConfigRef) =
|
||||||
conf.lastMsgWasDot.excl stdOrrKind
|
conf.lastMsgWasDot.excl stdOrrKind
|
||||||
write(stdOrr, "\n")
|
write(stdOrr, "\n")
|
||||||
|
|
||||||
proc toCChar*(c: char; result: var string) {.inline.} =
|
proc toCChar*(c: char; result: var string) =
|
||||||
case c
|
case c
|
||||||
of '\0'..'\x1F', '\x7F'..'\xFF':
|
of '\0'..'\x1F', '\x7F'..'\xFF':
|
||||||
result.add '\\'
|
result.add '\\'
|
||||||
|
|
@ -49,16 +40,18 @@ proc toCChar*(c: char; result: var string) {.inline.} =
|
||||||
result.add c
|
result.add c
|
||||||
|
|
||||||
proc makeCString*(s: string): Rope =
|
proc makeCString*(s: string): Rope =
|
||||||
result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
|
result = nil
|
||||||
result.add("\"")
|
var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
|
||||||
|
res.add("\"")
|
||||||
for i in 0..<s.len:
|
for i in 0..<s.len:
|
||||||
# line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
|
# line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
|
||||||
# It also makes reading c sources or grepping harder, for zero benefit.
|
# It also makes reading c sources or grepping harder, for zero benefit.
|
||||||
# const MaxLineLength = 64
|
# const MaxLineLength = 64
|
||||||
# if (i + 1) mod MaxLineLength == 0:
|
# if (i + 1) mod MaxLineLength == 0:
|
||||||
# res.add("\"\L\"")
|
# res.add("\"\L\"")
|
||||||
toCChar(s[i], result)
|
toCChar(s[i], res)
|
||||||
result.add('\"')
|
res.add('\"')
|
||||||
|
result.add(rope(res))
|
||||||
|
|
||||||
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
|
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
|
||||||
result.fullPath = fullPath
|
result.fullPath = fullPath
|
||||||
|
|
@ -82,7 +75,7 @@ when defined(nimpretty):
|
||||||
proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
|
proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
|
||||||
substr(conf.m.fileInfos[fid.int].fullContent, a, b)
|
substr(conf.m.fileInfos[fid.int].fullContent, a, b)
|
||||||
|
|
||||||
proc canonicalCase(path: var string) {.inline.} =
|
proc canonicalCase(path: var string) =
|
||||||
## the idea is to only use this for checking whether a path is already in
|
## the idea is to only use this for checking whether a path is already in
|
||||||
## the table but otherwise keep the original case
|
## the table but otherwise keep the original case
|
||||||
when FileSystemCaseSensitive: discard
|
when FileSystemCaseSensitive: discard
|
||||||
|
|
@ -105,13 +98,15 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
|
||||||
|
|
||||||
try:
|
try:
|
||||||
canon = canonicalizePath(conf, filename)
|
canon = canonicalizePath(conf, filename)
|
||||||
|
shallow(canon.string)
|
||||||
except OSError:
|
except OSError:
|
||||||
canon = filename
|
canon = filename
|
||||||
# The compiler uses "filenames" such as `command line` or `stdin`
|
# The compiler uses "filenames" such as `command line` or `stdin`
|
||||||
# This flag indicates that we are working with such a path here
|
# This flag indicates that we are working with such a path here
|
||||||
pseudoPath = true
|
pseudoPath = true
|
||||||
|
|
||||||
var canon2 = canon.string
|
var canon2: string
|
||||||
|
forceCopy(canon2, canon.string) # because `canon` may be shallow
|
||||||
canon2.canonicalCase
|
canon2.canonicalCase
|
||||||
|
|
||||||
if conf.m.filenameToIndexTbl.hasKey(canon2):
|
if conf.m.filenameToIndexTbl.hasKey(canon2):
|
||||||
|
|
@ -120,6 +115,7 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
|
||||||
else:
|
else:
|
||||||
isKnownFile = false
|
isKnownFile = false
|
||||||
result = conf.m.fileInfos.len.FileIndex
|
result = conf.m.fileInfos.len.FileIndex
|
||||||
|
#echo "ID ", result.int, " ", canon2
|
||||||
conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
|
conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
|
||||||
else: relativeTo(canon, conf.projectPath)))
|
else: relativeTo(canon, conf.projectPath)))
|
||||||
conf.m.filenameToIndexTbl[canon2] = result
|
conf.m.filenameToIndexTbl[canon2] = result
|
||||||
|
|
@ -128,13 +124,6 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
|
||||||
var dummy: bool
|
var dummy: bool
|
||||||
result = fileInfoIdx(conf, filename, dummy)
|
result = fileInfoIdx(conf, filename, dummy)
|
||||||
|
|
||||||
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
|
|
||||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
|
|
||||||
|
|
||||||
proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
|
|
||||||
var dummy: bool
|
|
||||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
|
|
||||||
|
|
||||||
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
||||||
result.fileIndex = fileInfoIdx
|
result.fileIndex = fileInfoIdx
|
||||||
if line < int high(uint16):
|
if line < int high(uint16):
|
||||||
|
|
@ -226,18 +215,11 @@ proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile)
|
||||||
|
|
||||||
proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
|
proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
|
||||||
assert fileIdx.int32 >= 0
|
assert fileIdx.int32 >= 0
|
||||||
when defined(gcArc) or defined(gcOrc):
|
shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
|
||||||
conf.m.fileInfos[fileIdx.int32].hash = hash
|
|
||||||
else:
|
|
||||||
shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
|
|
||||||
|
|
||||||
|
|
||||||
proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
|
proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||||
assert fileIdx.int32 >= 0
|
assert fileIdx.int32 >= 0
|
||||||
when defined(gcArc) or defined(gcOrc):
|
shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
|
||||||
result = conf.m.fileInfos[fileIdx.int32].hash
|
|
||||||
else:
|
|
||||||
shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
|
|
||||||
|
|
||||||
proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
|
proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile =
|
||||||
if fileIdx.int32 < 0:
|
if fileIdx.int32 < 0:
|
||||||
|
|
@ -307,6 +289,10 @@ proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool =
|
||||||
# only for debugging purposes
|
# only for debugging purposes
|
||||||
result = filename in toFilename(conf, info)
|
result = filename in toFilename(conf, info)
|
||||||
|
|
||||||
|
const
|
||||||
|
UnitSep = "\31"
|
||||||
|
# this needs care to avoid issues similar to https://github.com/nim-lang/Nim/issues/17853
|
||||||
|
|
||||||
type
|
type
|
||||||
MsgFlag* = enum ## flags altering msgWriteln behavior
|
MsgFlag* = enum ## flags altering msgWriteln behavior
|
||||||
msgStdout, ## force writing to stdout, even stderr is default
|
msgStdout, ## force writing to stdout, even stderr is default
|
||||||
|
|
@ -323,7 +309,7 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
|
||||||
## This is used for 'nim dump' etc. where we don't have nimsuggest
|
## This is used for 'nim dump' etc. where we don't have nimsuggest
|
||||||
## support.
|
## support.
|
||||||
#if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
|
#if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
|
||||||
let sep = if msgNoUnitSep notin flags: conf.unitSep else: ""
|
let sep = if msgNoUnitSep notin flags: UnitSep else: ""
|
||||||
if not isNil(conf.writelnHook) and msgSkipHook notin flags:
|
if not isNil(conf.writelnHook) and msgSkipHook notin flags:
|
||||||
conf.writelnHook(s & sep)
|
conf.writelnHook(s & sep)
|
||||||
elif optStdout in conf.globalOptions or msgStdout in flags:
|
elif optStdout in conf.globalOptions or msgStdout in flags:
|
||||||
|
|
@ -423,15 +409,15 @@ proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} =
|
||||||
styledMsgWriteln(fgRed, """
|
styledMsgWriteln(fgRed, """
|
||||||
No stack traceback available
|
No stack traceback available
|
||||||
To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 for details""" %
|
To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 for details""" %
|
||||||
[conf.command, "intern.html#debugging-the-compiler".createDocLink], conf.unitSep)
|
[conf.command, "intern.html#debugging-the-compiler".createDocLink], UnitSep)
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
|
proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) =
|
||||||
if msg in fatalMsgs:
|
if msg >= fatalMin and msg <= fatalMax:
|
||||||
if conf.cmd == cmdIdeTools: log(s)
|
if conf.cmd == cmdIdeTools: log(s)
|
||||||
quit(conf, msg)
|
quit(conf, msg)
|
||||||
if msg >= errMin and msg <= errMax or
|
if msg >= errMin and msg <= errMax or
|
||||||
(msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
|
(msg in warnMin..hintMax and msg in conf.warningAsErrors):
|
||||||
inc(conf.errorCounter)
|
inc(conf.errorCounter)
|
||||||
conf.exitcode = 1'i8
|
conf.exitcode = 1'i8
|
||||||
if conf.errorCounter >= conf.errorMax:
|
if conf.errorCounter >= conf.errorMax:
|
||||||
|
|
@ -511,18 +497,12 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
|
||||||
conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
|
conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
|
||||||
|
|
||||||
proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.gcsafe, noinline.} =
|
eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} =
|
||||||
var
|
var
|
||||||
title: string
|
title: string
|
||||||
color: ForegroundColor
|
color: ForegroundColor
|
||||||
ignoreMsg = false
|
ignoreMsg = false
|
||||||
sev: Severity
|
sev: Severity
|
||||||
let errorOutputsOld = conf.m.errorOutputs
|
|
||||||
if msg in fatalMsgs:
|
|
||||||
# don't gag, refs bug #7080, bug #18278; this can happen with `{.fatal.}`
|
|
||||||
# or inside a `tryConstExpr`.
|
|
||||||
conf.m.errorOutputs = {eStdOut, eStdErr}
|
|
||||||
|
|
||||||
let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special
|
let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special
|
||||||
case msg
|
case msg
|
||||||
of errMin..errMax:
|
of errMin..errMax:
|
||||||
|
|
@ -539,18 +519,19 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
of warnMin..warnMax:
|
of warnMin..warnMax:
|
||||||
sev = Severity.Warning
|
sev = Severity.Warning
|
||||||
ignoreMsg = not conf.hasWarn(msg)
|
ignoreMsg = not conf.hasWarn(msg)
|
||||||
if not ignoreMsg and msg in conf.warningAsErrors:
|
if msg in conf.warningAsErrors:
|
||||||
|
ignoreMsg = false
|
||||||
title = ErrorTitle
|
title = ErrorTitle
|
||||||
color = ErrorColor
|
|
||||||
else:
|
else:
|
||||||
title = WarningTitle
|
title = WarningTitle
|
||||||
color = WarningColor
|
|
||||||
if not ignoreMsg: writeContext(conf, info)
|
if not ignoreMsg: writeContext(conf, info)
|
||||||
|
color = WarningColor
|
||||||
inc(conf.warnCounter)
|
inc(conf.warnCounter)
|
||||||
of hintMin..hintMax:
|
of hintMin..hintMax:
|
||||||
sev = Severity.Hint
|
sev = Severity.Hint
|
||||||
ignoreMsg = not conf.hasHint(msg)
|
ignoreMsg = not conf.hasHint(msg)
|
||||||
if not ignoreMsg and msg in conf.warningAsErrors:
|
if msg in conf.warningAsErrors:
|
||||||
|
ignoreMsg = false
|
||||||
title = ErrorTitle
|
title = ErrorTitle
|
||||||
else:
|
else:
|
||||||
title = HintTitle
|
title = HintTitle
|
||||||
|
|
@ -569,17 +550,14 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||||
msgWrite(conf, ".")
|
msgWrite(conf, ".")
|
||||||
else:
|
else:
|
||||||
styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg,
|
styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg,
|
||||||
resetStyle, conf.getSurroundingSrc(info), conf.unitSep)
|
resetStyle, conf.getSurroundingSrc(info), UnitSep)
|
||||||
if hintMsgOrigin in conf.mainPackageNotes:
|
if hintMsgOrigin in conf.mainPackageNotes:
|
||||||
# xxx needs a bit of refactoring to honor `conf.filenameOption`
|
# xxx needs a bit of refactoring to honor `conf.filenameOption`
|
||||||
styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
|
styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
|
||||||
" compiler msg initiated here", KindColor,
|
" compiler msg initiated here", KindColor,
|
||||||
KindFormat % $hintMsgOrigin,
|
KindFormat % $hintMsgOrigin,
|
||||||
resetStyle, conf.unitSep)
|
resetStyle, UnitSep)
|
||||||
handleError(conf, msg, eh, s, ignoreMsg)
|
handleError(conf, msg, eh, s)
|
||||||
if msg in fatalMsgs:
|
|
||||||
# most likely would have died here but just in case, we restore state
|
|
||||||
conf.m.errorOutputs = errorOutputsOld
|
|
||||||
|
|
||||||
template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
|
template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
|
||||||
let arg = msgKindToString(msg) % args
|
let arg = msgKindToString(msg) % args
|
||||||
|
|
@ -588,7 +566,9 @@ template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
|
||||||
template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
|
template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) =
|
||||||
liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
|
liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc())
|
||||||
|
|
||||||
template fatal*(conf: ConfigRef; info: TLineInfo, arg = "", msg = errFatal) =
|
template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||||
|
# this fixes bug #7080 so that it is at least obvious 'fatal' was executed.
|
||||||
|
conf.m.errorOutputs = {eStdOut, eStdErr}
|
||||||
liMessage(conf, info, msg, arg, doAbort, instLoc())
|
liMessage(conf, info, msg, arg, doAbort, instLoc())
|
||||||
|
|
||||||
template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
|
template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
|
||||||
|
|
@ -636,9 +616,9 @@ template internalAssert*(conf: ConfigRef, e: bool) =
|
||||||
let arg = info2.toFileLineCol
|
let arg = info2.toFileLineCol
|
||||||
internalErrorImpl(conf, unknownLineInfo, arg, info2)
|
internalErrorImpl(conf, unknownLineInfo, arg, info2)
|
||||||
|
|
||||||
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
|
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, forceHint = false, extraMsg = "") =
|
||||||
let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
|
let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
|
||||||
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
|
let msg = if optStyleError in conf.globalOptions and not forceHint: errGenerated else: hintName
|
||||||
liMessage(conf, info, msg, m, doNothing, instLoc())
|
liMessage(conf, info, msg, m, doNothing, instLoc())
|
||||||
|
|
||||||
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
||||||
|
|
@ -680,54 +660,3 @@ proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
|
||||||
# We mangle upper letters and digits too so that there cannot
|
# We mangle upper letters and digits too so that there cannot
|
||||||
# be clashes with our special meanings of 'Z' and 'O'
|
# be clashes with our special meanings of 'Z' and 'O'
|
||||||
result.addInt ord(c)
|
result.addInt ord(c)
|
||||||
|
|
||||||
proc genSuccessX*(conf: ConfigRef) =
|
|
||||||
let mem =
|
|
||||||
when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
|
|
||||||
else: formatSize(getTotalMem()) & " totmem"
|
|
||||||
let loc = $conf.linesCompiled
|
|
||||||
var build = ""
|
|
||||||
var flags = ""
|
|
||||||
const debugModeHints = "none (DEBUG BUILD, `-d:release` generates faster code)"
|
|
||||||
if conf.cmd in cmdBackends:
|
|
||||||
if conf.backend != backendJs:
|
|
||||||
build.add "mm: $#; " % $conf.selectedGC
|
|
||||||
if optThreads in conf.globalOptions: build.add "threads: on; "
|
|
||||||
build.add "opt: "
|
|
||||||
if optOptimizeSpeed in conf.options: build.add "speed"
|
|
||||||
elif optOptimizeSize in conf.options: build.add "size"
|
|
||||||
else: build.add debugModeHints
|
|
||||||
# pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html
|
|
||||||
if isDefined(conf, "danger"): flags.add " -d:danger"
|
|
||||||
elif isDefined(conf, "release"): flags.add " -d:release"
|
|
||||||
else:
|
|
||||||
build.add "opt: "
|
|
||||||
if isDefined(conf, "danger"):
|
|
||||||
build.add "speed"
|
|
||||||
flags.add " -d:danger"
|
|
||||||
elif isDefined(conf, "release"):
|
|
||||||
build.add "speed"
|
|
||||||
flags.add " -d:release"
|
|
||||||
else: build.add debugModeHints
|
|
||||||
if flags.len > 0: build.add "; options:" & flags
|
|
||||||
let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3)
|
|
||||||
let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName
|
|
||||||
# xxx honor conf.filenameOption more accurately
|
|
||||||
var output: string
|
|
||||||
if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript:
|
|
||||||
output = $conf.jsonBuildFile
|
|
||||||
elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
|
|
||||||
# for some cmd we expect a valid absOutFile
|
|
||||||
output = "unknownOutput"
|
|
||||||
else:
|
|
||||||
output = $conf.absOutFile
|
|
||||||
if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename
|
|
||||||
# xxx honor filenameOption more accurately
|
|
||||||
rawMessage(conf, hintSuccessX, [
|
|
||||||
"build", build,
|
|
||||||
"loc", loc,
|
|
||||||
"sec", sec,
|
|
||||||
"mem", mem,
|
|
||||||
"project", project,
|
|
||||||
"output", output,
|
|
||||||
])
|
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
|
|
||||||
import ast, msgs, ropes, options, pathutils
|
import ast, msgs, ropes, options, pathutils
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[syncio, assertions]
|
|
||||||
|
|
||||||
type
|
type
|
||||||
NdiFile* = object
|
NdiFile* = object
|
||||||
enabled: bool
|
enabled: bool
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue