Compare commits

..

1 commit

Author SHA1 Message Date
Joey
07fb6df6bc
Add checked to dom
This allows the ability to set a checkbox as checked programmatically. It's different from `setAttribute` because once an input has been clicked on by the user, `setAttribute` no longer works programmatically.
2021-05-16 20:44:35 -06:00
1583 changed files with 41115 additions and 67685 deletions

37
.builds/freebsd.yml Normal file
View 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
View 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
View 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
View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

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

View file

@ -1,6 +1,4 @@
# xxx unused, out of date image: ubuntu:16.04
image: ubuntu:18.04
stages: stages:
- pre-build - pre-build

View file

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

View file

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

@ -0,0 +1 @@
nim-gdb

View file

@ -3,7 +3,7 @@ for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i)
for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi) 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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