mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-08 22:47:27 +00:00
merge
This commit is contained in:
@@ -13,7 +13,7 @@ packages:
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
NIM_TESTAMENT_BATCH: "0_2"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
|
||||
@@ -13,7 +13,7 @@ packages:
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
NIM_TESTAMENT_BATCH: "1_2"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
## do not 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: "2_3"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
cd Nim
|
||||
git clone --depth 1 -q https://github.com/nim-lang/csources.git
|
||||
gmake -C csources -j $(sysctl -n hw.ncpuonline)
|
||||
bin/nim c koch
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
cd Nim
|
||||
if ! ./koch runCI; then
|
||||
nim c -r tools/ci_testresults.nim
|
||||
exit 1
|
||||
fi
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
contact_links:
|
||||
- name: Forum
|
||||
url: https://forum.nim-lang.org
|
||||
about: Please ask and answer questions here.
|
||||
- name: Discord
|
||||
url: https://discord.gg/nim
|
||||
about: Please ask and answer questions here.
|
||||
- name: Stack Overflow
|
||||
url: https://stackoverflow.com/questions/tagged/nim-lang
|
||||
about: Please ask and answer questions here.
|
||||
6
.github/workflows/ci_docs.yml
vendored
6
.github/workflows/ci_docs.yml
vendored
@@ -26,7 +26,7 @@ on:
|
||||
- 'tools/dochack/dochack.nim'
|
||||
- 'tools/kochdocs.nim'
|
||||
- '.github/workflows/ci_docs.yml'
|
||||
|
||||
- 'koch.nim'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -126,10 +126,6 @@ jobs:
|
||||
shell: bash
|
||||
run: ./koch boot -d:release
|
||||
|
||||
- name: 'Clone fusion'
|
||||
shell: bash
|
||||
run: ./koch fusion
|
||||
|
||||
- name: 'Build documentation'
|
||||
shell: bash
|
||||
run: ./koch doc --git.commit:devel
|
||||
|
||||
7
.github/workflows/ci_packages.yml
vendored
7
.github/workflows/ci_packages.yml
vendored
@@ -10,11 +10,12 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-10.15]
|
||||
cpu: [amd64]
|
||||
pkg: [1, 2]
|
||||
name: '${{ matrix.os }} (pkg: ${{ matrix.pkg }})'
|
||||
batch: ["0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
NIM_TEST_PACKAGES: ${{ matrix.pkg }}
|
||||
NIM_TEST_PACKAGES: "1"
|
||||
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v2
|
||||
|
||||
93
.github/workflows/ci_ssl.yml
vendored
93
.github/workflows/ci_ssl.yml
vendored
@@ -1,93 +0,0 @@
|
||||
name: Nim SSL CI
|
||||
on:
|
||||
pull_request:
|
||||
# Run only on changes on related files
|
||||
paths:
|
||||
- 'lib/pure/httpclient.nim'
|
||||
- 'lib/pure/net.nim'
|
||||
- 'lib/pure/ssl_certs.nim'
|
||||
- 'lib/wrappers/openssl.nim'
|
||||
- 'tests/stdlib/thttpclient_ssl*'
|
||||
- 'tests/untestable/thttpclient_ssl*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: |
|
||||
!contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-10.15, windows-2019]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }} (${{ matrix.cpu }})'
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 'Checkout csources'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: nim-lang/csources
|
||||
path: csources
|
||||
|
||||
- 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 -y libssl1.1
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install make
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir dist
|
||||
curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
|
||||
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
7z x dist/mingw64.7z -odist
|
||||
7z x dist/dlls.zip -obin
|
||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Build 1-stage compiler from csources'
|
||||
shell: bash
|
||||
run: |
|
||||
ncpu=
|
||||
case '${{ runner.os }}' in
|
||||
'Linux')
|
||||
ncpu=$(nproc)
|
||||
;;
|
||||
'macOS')
|
||||
ncpu=$(sysctl -n hw.ncpu)
|
||||
;;
|
||||
'Windows')
|
||||
ncpu=$NUMBER_OF_PROCESSORS
|
||||
;;
|
||||
esac
|
||||
[[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1
|
||||
|
||||
make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'Build koch'
|
||||
shell: bash
|
||||
run: nim c koch
|
||||
|
||||
- name: 'Build the real compiler'
|
||||
shell: bash
|
||||
run: ./koch boot
|
||||
|
||||
- name: 'Run SSL nimDisableCertificateValidation integration tests'
|
||||
shell: bash
|
||||
run: nim c -d:nimDisableCertificateValidation -d:ssl -r -p:. tests/untestable/thttpclient_ssl_disabled.nim
|
||||
|
||||
- name: 'Run SSL certificate check integration tests'
|
||||
# Not supported on Windows due to old openssl version
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: nim c -d:ssl -p:. --threads:on -r tests/untestable/thttpclient_ssl.nim
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -30,7 +30,7 @@ deinstall.sh
|
||||
|
||||
doc/html/
|
||||
doc/*.html
|
||||
doc/*.pdf
|
||||
doc/pdf
|
||||
doc/*.idx
|
||||
/web/upload
|
||||
/build/*
|
||||
@@ -66,7 +66,7 @@ testament.db
|
||||
/tests/**/*.js
|
||||
/csources
|
||||
/dist/
|
||||
/lib/fusion
|
||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
||||
|
||||
# Private directories and files (IDEs)
|
||||
.*/
|
||||
@@ -100,3 +100,5 @@ htmldocs
|
||||
|
||||
## these are not needed anymore unless checkout old older versions
|
||||
nimdoc.out.css
|
||||
# except here:
|
||||
!/nimdoc/testproject/expected/*
|
||||
|
||||
@@ -54,7 +54,7 @@ test-windows:
|
||||
script:
|
||||
- call ci\deps.bat
|
||||
- nim c testament\tester
|
||||
- testament\tester.exe --pedantic all
|
||||
- testament\tester.exe all
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
|
||||
@@ -17,9 +17,11 @@ jobs:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
CPU: amd64
|
||||
Linux_i386:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
CPU: i386
|
||||
# Linux_i386:
|
||||
# # 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
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-10.15'
|
||||
CPU: amd64
|
||||
@@ -64,29 +66,34 @@ jobs:
|
||||
displayName: 'Install node.js 12.x'
|
||||
|
||||
- bash: |
|
||||
sudo apt-fast update -qq
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
displayName: 'Install dependencies (amd64 Linux)'
|
||||
condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
- bash: |
|
||||
sudo dpkg --add-architecture i386
|
||||
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo dpkg --add-architecture i386
|
||||
# Downgrade llvm:
|
||||
# - llvm has to be downgraded to have 32bit version installed for sfml.
|
||||
|
||||
cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
|
||||
Package: libllvm6.0
|
||||
Pin: origin "azure.archive.ubuntu.com"
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
sudo apt-fast update -qq
|
||||
# echo_run sudo apt-fast update -qq
|
||||
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
|
||||
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
|
||||
# `could not load: libffi.so` during dynamic loading.
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
||||
|
||||
@@ -95,14 +102,15 @@ jobs:
|
||||
|
||||
exec $(which gcc) -m32 "\$@"
|
||||
EOF
|
||||
|
||||
cat << EOF > bin/g++
|
||||
#!/bin/bash
|
||||
|
||||
exec $(which g++) -m32 "\$@"
|
||||
EOF
|
||||
|
||||
chmod 755 bin/gcc
|
||||
chmod 755 bin/g++
|
||||
echo_run chmod 755 bin/gcc
|
||||
echo_run chmod 755 bin/g++
|
||||
|
||||
displayName: 'Install dependencies (i386 Linux)'
|
||||
condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386'))
|
||||
@@ -112,12 +120,14 @@ jobs:
|
||||
condition: eq(variables['Agent.OS'], 'Darwin')
|
||||
|
||||
- bash: |
|
||||
mkdir dist
|
||||
curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
|
||||
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
7z x dist/mingw64.7z -odist
|
||||
7z x dist/dlls.zip -obin
|
||||
echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
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'
|
||||
|
||||
displayName: 'Install dependencies (Windows)'
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
@@ -126,13 +136,15 @@ jobs:
|
||||
displayName: 'Add build binaries to PATH'
|
||||
|
||||
- bash: |
|
||||
echo 'PATH:' "$PATH"
|
||||
echo '##[section]gcc version'
|
||||
gcc -v
|
||||
echo '##[section]nodejs version'
|
||||
node -v
|
||||
echo '##[section]make version'
|
||||
make -v
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run echo 'PATH:' "$PATH"
|
||||
echo_run echo '##[section]gcc version'
|
||||
echo_run gcc -v
|
||||
echo_run echo '##[section]nodejs version'
|
||||
echo_run node -v
|
||||
echo_run echo '##[section]make version'
|
||||
echo_run make -v
|
||||
displayName: 'System information'
|
||||
|
||||
- bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)"
|
||||
@@ -145,6 +157,8 @@ jobs:
|
||||
displayName: 'Restore built csources'
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
ncpu=
|
||||
ext=
|
||||
case '$(Agent.OS)' in
|
||||
@@ -162,12 +176,12 @@ jobs:
|
||||
[[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1
|
||||
|
||||
if [[ -x csources/bin/nim$ext ]]; then
|
||||
echo "Found cached compiler, skipping build"
|
||||
echo_run echo "Found cached compiler, skipping build"
|
||||
else
|
||||
make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no
|
||||
echo_run make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no
|
||||
fi
|
||||
|
||||
cp csources/bin/nim$ext bin
|
||||
echo_run cp csources/bin/nim$ext bin
|
||||
displayName: 'Build 1-stage compiler from csources'
|
||||
|
||||
- bash: nim c koch
|
||||
|
||||
224
changelog.md
224
changelog.md
@@ -4,15 +4,28 @@
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
- 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.
|
||||
|
||||
|
||||
- Make `{.requiresInit.}` pragma to work for `distinct` types.
|
||||
|
||||
- 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
|
||||
@@ -22,32 +35,49 @@
|
||||
literals remain in the "raw" string form so that client code can easily treat
|
||||
small and large numbers uniformly.
|
||||
|
||||
- Added `BackwardsIndex` overload for `JsonNode`.
|
||||
|
||||
- added `jsonutils.jsonTo` overload with `opt = Joptions()` param.
|
||||
|
||||
- `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 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 std/enumutils module containing `genEnumCaseStmt` macro that generates
|
||||
case statement to parse string to enum.
|
||||
- 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.
|
||||
|
||||
- ``--gc:orc`` is now 10% faster than previously for common workloads. If
|
||||
you have trouble with its changed behavior, compile with ``-d:nimOldOrc``.
|
||||
- `--gc:orc` is now 10% faster than previously for common workloads. If
|
||||
you have trouble with its changed behavior, compile with `-d:nimOldOrc`.
|
||||
|
||||
|
||||
- `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.
|
||||
|
||||
- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent,
|
||||
see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior.
|
||||
|
||||
@@ -55,27 +85,162 @@
|
||||
|
||||
- `writeStackTrace` is available in JS backend now.
|
||||
|
||||
- `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 `decodeQuery` to `std/uri`.
|
||||
|
||||
- Added `setutils.toSet` that can take any iterable and convert it to a built-in set,
|
||||
- `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`.
|
||||
|
||||
- `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.
|
||||
|
||||
- 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 `sequtils` import to `prelude`.
|
||||
|
||||
- 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
|
||||
|
||||
- `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.
|
||||
|
||||
- Added `socketstream` module that wraps sockets in the stream interface
|
||||
|
||||
- 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.
|
||||
|
||||
- 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 `std/sysrand` module to get random numbers from a secure source
|
||||
provided by the operating system.
|
||||
|
||||
- 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 `random.initRand()` overload with no argument which uses the current time as a seed.
|
||||
|
||||
- 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.
|
||||
|
||||
- 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.
|
||||
|
||||
- Added `system.prepareStrMutation` for better support of low
|
||||
level `moveMem`, `copyMem` operations for Orc's copy-on-write string
|
||||
implementation.
|
||||
|
||||
- `hashes.hash` now supports `object`, but can be overloaded.
|
||||
|
||||
- Added `std/strbasics` for high performance string operations.
|
||||
Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`.
|
||||
|
||||
- `hashes.hash` now supports `object`, but can be overloaded.
|
||||
|
||||
- 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 `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`.
|
||||
|
||||
- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`,
|
||||
unless `-d:nimLegacyHomeDir` is specified (for a transition period).
|
||||
|
||||
- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
|
||||
|
||||
- Added dollar `$` and `len` for `jsre.RegExp`.
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
@@ -85,20 +250,58 @@
|
||||
|
||||
- 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.
|
||||
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
- 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)
|
||||
|
||||
- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous
|
||||
behavior.
|
||||
|
||||
- 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`.
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
@@ -107,3 +310,6 @@
|
||||
- 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.
|
||||
|
||||
8
ci/funs.sh
Normal file
8
ci/funs.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
# utilities used in CI pipelines to avoid duplication.
|
||||
|
||||
echo_run () {
|
||||
# echo's a command before running it, which helps understanding logs
|
||||
echo ""
|
||||
echo "$@"
|
||||
"$@"
|
||||
}
|
||||
206
compiler/ast.nim
206
compiler/ast.nim
@@ -221,6 +221,9 @@ type
|
||||
nkBreakState, # special break statement for easier code generation
|
||||
nkFuncDef, # a func
|
||||
nkTupleConstr # a tuple constructor
|
||||
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
|
||||
nkReplayAction # for .rod file support: A replay action
|
||||
nkNilRodNode # for .rod file support: a 'nil' PNode
|
||||
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
@@ -262,6 +265,10 @@ type
|
||||
sfShadowed, # a symbol that was shadowed in some inner scope
|
||||
sfThread, # proc will run as a thread
|
||||
# variable is a thread variable
|
||||
sfCppNonPod, # tells compiler to treat such types as non-pod's, so that
|
||||
# `thread_local` is used instead of `__thread` for
|
||||
# {.threadvar.} + `--threads`. Only makes sense for importcpp types.
|
||||
# This has a performance impact so isn't set by default.
|
||||
sfCompileTime, # proc can be evaluated at compile time
|
||||
sfConstructor, # proc is a C++ constructor
|
||||
sfDispatcher, # copied method symbol is the dispatcher
|
||||
@@ -427,9 +434,8 @@ type
|
||||
# instantiation and prior to this it has the potential to
|
||||
# be any type.
|
||||
|
||||
tyOptDeprecated
|
||||
# 'out' parameter. Comparable to a 'var' parameter but every
|
||||
# path must assign a value to it before it can be read from.
|
||||
tyConcept
|
||||
# new style concept.
|
||||
|
||||
tyVoid
|
||||
# now different from tyEmpty, hurray!
|
||||
@@ -484,6 +490,8 @@ type
|
||||
nfDefaultRefsParam # a default param value references another parameter
|
||||
# the flag is applied to proc default values and to calls
|
||||
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
||||
nfLastRead # this node is a last read
|
||||
nfFirstWrite# this node is a first write
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40)
|
||||
@@ -717,6 +725,16 @@ type
|
||||
module*: int32
|
||||
item*: int32
|
||||
|
||||
proc `==`*(a, b: ItemId): bool {.inline.} =
|
||||
a.item == b.item and a.module == b.module
|
||||
|
||||
proc hash*(x: ItemId): Hash =
|
||||
var h: Hash = hash(x.module)
|
||||
h = h !& hash(x.item)
|
||||
result = !$h
|
||||
|
||||
|
||||
type
|
||||
TIdObj* = object of RootObj
|
||||
itemId*: ItemId
|
||||
PIdObj* = ref TIdObj
|
||||
@@ -827,24 +845,10 @@ type
|
||||
TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
|
||||
# proc and type instantiations are cached in the generic symbol
|
||||
case kind*: TSymKind
|
||||
of skType, skGenericParam:
|
||||
typeInstCache*: seq[PType]
|
||||
of routineKinds:
|
||||
procInstCache*: seq[PInstantiation]
|
||||
#procInstCache*: seq[PInstantiation]
|
||||
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
|
||||
transformedBody*: PNode # cached body after transf pass
|
||||
of skModule, skPackage:
|
||||
# modules keep track of the generic symbols they use from other modules.
|
||||
# this is because in incremental compilation, when a module is about to
|
||||
# be replaced with a newer version, we must decrement the usage count
|
||||
# of all previously used generics.
|
||||
# For 'import as' we copy the module symbol but shallowCopy the 'tab'
|
||||
# and set the 'usedGenerics' to ... XXX gah! Better set module.name
|
||||
# instead? But this doesn't work either. --> We need an skModuleAlias?
|
||||
# No need, just leave it as skModule but set the owner accordingly and
|
||||
# check for the owner when touching 'usedGenerics'.
|
||||
usedGenerics*: seq[PInstantiation]
|
||||
tab*: TStrTable # interface table for modules
|
||||
of skLet, skVar, skField, skForVar:
|
||||
guard*: PSym
|
||||
bitsize*: int
|
||||
@@ -922,8 +926,6 @@ type
|
||||
owner*: PSym # the 'owner' of the type
|
||||
sym*: PSym # types have the sym associated with them
|
||||
# it is used for converting types to strings
|
||||
attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc.
|
||||
methods*: seq[(int,PSym)] # attached methods
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
# -1 means that the size is unkwown
|
||||
align*: int16 # the type's alignment requirements
|
||||
@@ -933,7 +935,7 @@ type
|
||||
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
||||
# type.
|
||||
uniqueId*: ItemId # due to a design mistake, we need to keep the real ID here as it
|
||||
# required by the --incremental:on mode.
|
||||
# is required by the --incremental:on mode.
|
||||
|
||||
TPair* = object
|
||||
key*, val*: RootRef
|
||||
@@ -1004,7 +1006,7 @@ const
|
||||
ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
|
||||
tyTuple, tySequence}
|
||||
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
|
||||
tyProc, tyError}
|
||||
tyProc, tyError} # TODO
|
||||
PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM
|
||||
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
|
||||
skIterator,
|
||||
@@ -1013,7 +1015,7 @@ const
|
||||
nfDotSetter, nfDotField,
|
||||
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload}
|
||||
nfExecuteOnReload, nfLastRead, nfFirstWrite}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -1038,6 +1040,7 @@ const
|
||||
declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
||||
routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef}
|
||||
procDefs* = nkLambdaKinds + declarativeDefs
|
||||
callableDefs* = nkLambdaKinds + routineDefs
|
||||
|
||||
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
|
||||
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
||||
@@ -1074,18 +1077,33 @@ template id*(a: PIdObj): int =
|
||||
(x.itemId.module.int shl moduleShift) + x.itemId.item.int
|
||||
|
||||
type
|
||||
IdGenerator* = ref ItemId # unfortunately, we really need the 'shared mutable' aspect here.
|
||||
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
|
||||
module*: int32
|
||||
symId*: int32
|
||||
typeId*: int32
|
||||
sealed*: bool
|
||||
|
||||
const
|
||||
PackageModuleId* = -3'i32
|
||||
|
||||
proc idGeneratorFromModule*(m: PSym): IdGenerator =
|
||||
assert m.kind == skModule
|
||||
result = IdGenerator(module: m.itemId.module, item: m.itemId.item)
|
||||
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||
|
||||
proc nextId*(x: IdGenerator): ItemId {.inline.} =
|
||||
inc x.item
|
||||
result = x[]
|
||||
proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
|
||||
assert(not x.sealed)
|
||||
inc x.symId
|
||||
result = ItemId(module: x.module, item: x.symId)
|
||||
|
||||
proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
|
||||
assert(not x.sealed)
|
||||
inc x.typeId
|
||||
result = ItemId(module: x.module, item: x.typeId)
|
||||
|
||||
when false:
|
||||
proc nextId*(x: IdGenerator): ItemId {.inline.} =
|
||||
inc x.item
|
||||
result = x[]
|
||||
|
||||
when false:
|
||||
proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} =
|
||||
@@ -1111,11 +1129,7 @@ proc discardSons*(father: PNode)
|
||||
type Indexable = PNode | PType
|
||||
|
||||
proc len*(n: Indexable): int {.inline.} =
|
||||
when defined(nimNoNilSeqs):
|
||||
result = n.sons.len
|
||||
else:
|
||||
if isNil(n.sons): result = 0
|
||||
else: result = n.sons.len
|
||||
result = n.sons.len
|
||||
|
||||
proc safeLen*(n: PNode): int {.inline.} =
|
||||
## works even for leaves.
|
||||
@@ -1130,8 +1144,9 @@ proc safeArrLen*(n: PNode): int {.inline.} =
|
||||
|
||||
proc add*(father, son: Indexable) =
|
||||
assert son != nil
|
||||
when not defined(nimNoNilSeqs):
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
father.sons.add(son)
|
||||
|
||||
proc addAllowNil*(father, son: Indexable) {.inline.} =
|
||||
father.sons.add(son)
|
||||
|
||||
template `[]`*(n: Indexable, i: int): Indexable = n.sons[i]
|
||||
@@ -1199,11 +1214,32 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[
|
||||
template previouslyInferred*(t: PType): PType =
|
||||
if t.sons.len > 1: t.lastSon else: nil
|
||||
|
||||
when false:
|
||||
import tables, strutils
|
||||
var x: CountTable[string]
|
||||
|
||||
addQuitProc proc () {.noconv.} =
|
||||
for k, v in pairs(x):
|
||||
echo k
|
||||
echo v
|
||||
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
|
||||
info: TLineInfo; options: TOptions = {}): PSym =
|
||||
# generates a symbol and initializes the hash field too
|
||||
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
||||
options: options, owner: owner, offset: defaultOffset)
|
||||
when false:
|
||||
if id.item > 2141:
|
||||
let s = getStackTrace()
|
||||
const words = ["createTypeBoundOps",
|
||||
"initOperators",
|
||||
"generateInstance",
|
||||
"semIdentDef", "addLocalDecl"]
|
||||
for w in words:
|
||||
if w in s:
|
||||
x.inc w
|
||||
return
|
||||
x.inc "<no category>"
|
||||
|
||||
proc astdef*(s: PSym): PNode =
|
||||
# get only the definition (initializer) portion of the ast
|
||||
@@ -1268,10 +1304,7 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
|
||||
for i in 0..high(src.data): dest.data[i] = src.data[i]
|
||||
|
||||
proc discardSons*(father: PNode) =
|
||||
when defined(nimNoNilSeqs):
|
||||
father.sons = @[]
|
||||
else:
|
||||
father.sons = nil
|
||||
father.sons = @[]
|
||||
|
||||
proc withInfo*(n: PNode, info: TLineInfo): PNode =
|
||||
n.info = info
|
||||
@@ -1383,10 +1416,11 @@ proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
||||
lockLevel: UnspecifiedLockLevel,
|
||||
uniqueId: id)
|
||||
when false:
|
||||
if result.id == 76426:
|
||||
if result.itemId.module == 55 and result.itemId.item == 2:
|
||||
echo "KNID ", kind
|
||||
writeStackTrace()
|
||||
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.k == low(typeof(a.k)): a.k = b.k
|
||||
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
||||
@@ -1395,13 +1429,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.r == nil: a.r = b.r
|
||||
|
||||
proc newSons*(father: Indexable, length: int) =
|
||||
when defined(nimNoNilSeqs):
|
||||
setLen(father.sons, length)
|
||||
else:
|
||||
if isNil(father.sons):
|
||||
newSeq(father.sons, length)
|
||||
else:
|
||||
setLen(father.sons, length)
|
||||
setLen(father.sons, length)
|
||||
|
||||
proc assignType*(dest, src: PType) =
|
||||
dest.kind = src.kind
|
||||
@@ -1410,7 +1438,6 @@ proc assignType*(dest, src: PType) =
|
||||
dest.n = src.n
|
||||
dest.size = src.size
|
||||
dest.align = src.align
|
||||
dest.attachedOps = src.attachedOps
|
||||
dest.lockLevel = src.lockLevel
|
||||
# this fixes 'type TLock = TSysLock':
|
||||
if src.sym != nil:
|
||||
@@ -1437,8 +1464,6 @@ proc copySym*(s: PSym; id: ItemId): PSym =
|
||||
result.typ = s.typ
|
||||
result.flags = s.flags
|
||||
result.magic = s.magic
|
||||
if s.kind == skModule:
|
||||
copyStrTable(result.tab, s.tab)
|
||||
result.options = s.options
|
||||
result.position = s.position
|
||||
result.loc = s.loc
|
||||
@@ -1456,13 +1481,10 @@ proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
|
||||
result.ast = s.ast
|
||||
#result.id = s.id # XXX figure out what to do with the ID.
|
||||
result.flags = s.flags
|
||||
system.shallowCopy(result.tab, s.tab)
|
||||
result.options = s.options
|
||||
result.position = s.position
|
||||
result.loc = s.loc
|
||||
result.annex = s.annex
|
||||
# XXX once usedGenerics is used, ensure module aliases keep working!
|
||||
assert s.usedGenerics.len == 0
|
||||
|
||||
proc initStrTable*(x: var TStrTable) =
|
||||
x.counter = 0
|
||||
@@ -1541,26 +1563,17 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
owner.flags.incl tfHasGCedMem
|
||||
|
||||
proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
|
||||
when not defined(nimNoNilSeqs):
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
father.sons.add(son)
|
||||
if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
|
||||
|
||||
proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) =
|
||||
when not defined(nimNoNilSeqs):
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
father.sons.add(son)
|
||||
|
||||
proc addSonNilAllowed*(father, son: PNode) =
|
||||
when not defined(nimNoNilSeqs):
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
father.sons.add(son)
|
||||
|
||||
proc delSon*(father: PNode, idx: int) =
|
||||
when defined(nimNoNilSeqs):
|
||||
if father.len == 0: return
|
||||
else:
|
||||
if isNil(father.sons): return
|
||||
if father.len == 0: return
|
||||
for i in idx..<father.len - 1: father[i] = father[i + 1]
|
||||
father.sons.setLen(father.len - 1)
|
||||
|
||||
@@ -1615,11 +1628,9 @@ template transitionSymKindCommon*(k: TSymKind) =
|
||||
|
||||
proc transitionGenericParamToType*(s: PSym) =
|
||||
transitionSymKindCommon(skType)
|
||||
s.typeInstCache = obj.typeInstCache
|
||||
|
||||
proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) =
|
||||
transitionSymKindCommon(kind)
|
||||
s.procInstCache = obj.procInstCache
|
||||
s.gcUnsafetyReason = obj.gcUnsafetyReason
|
||||
s.transformedBody = obj.transformedBody
|
||||
|
||||
@@ -1733,10 +1744,7 @@ proc getStr*(a: PNode): string =
|
||||
of nkStrLit..nkTripleStrLit: result = a.strVal
|
||||
of nkNilLit:
|
||||
# let's hope this fixes more problems than it creates:
|
||||
when defined(nimNoNilSeqs):
|
||||
result = ""
|
||||
else:
|
||||
result = nil
|
||||
result = ""
|
||||
else:
|
||||
raiseRecoverableError("cannot extract string from invalid AST node")
|
||||
#doAssert false, "getStr"
|
||||
@@ -1753,12 +1761,32 @@ proc getStrOrChar*(a: PNode): string =
|
||||
#internalError(a.info, "getStrOrChar")
|
||||
#result = ""
|
||||
|
||||
proc isGenericRoutine*(s: PSym): bool =
|
||||
case s.kind
|
||||
of skProcKinds:
|
||||
result = sfFromGeneric in s.flags or
|
||||
(s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty)
|
||||
else: discard
|
||||
proc isGenericParams*(n: PNode): bool {.inline.} =
|
||||
## used to judge whether a node is generic params.
|
||||
n != nil and n.kind == nkGenericParams
|
||||
|
||||
proc isGenericRoutine*(n: PNode): bool {.inline.} =
|
||||
n != nil and n.kind in callableDefs and n[genericParamsPos].isGenericParams
|
||||
|
||||
proc isGenericRoutineStrict*(s: PSym): bool {.inline.} =
|
||||
## determines if this symbol represents a generic routine
|
||||
## the unusual name is so it doesn't collide and eventually replaces
|
||||
## `isGenericRoutine`
|
||||
s.kind in skProcKinds and s.ast.isGenericRoutine
|
||||
|
||||
proc isGenericRoutine*(s: PSym): bool {.inline.} =
|
||||
## determines if this symbol represents a generic routine or an instance of
|
||||
## one. This should be renamed accordingly and `isGenericRoutineStrict`
|
||||
## should take this name instead.
|
||||
##
|
||||
## Warning/XXX: Unfortunately, it considers a proc kind symbol flagged with
|
||||
## sfFromGeneric as a generic routine. Instead this should likely not be the
|
||||
## case and the concepts should be teased apart:
|
||||
## - generic definition
|
||||
## - generic instance
|
||||
## - either generic definition or instance
|
||||
s.kind in skProcKinds and (sfFromGeneric in s.flags or
|
||||
s.ast.isGenericRoutine)
|
||||
|
||||
proc skipGenericOwner*(s: PSym): PSym =
|
||||
## Generic instantiations are owned by their originating generic
|
||||
@@ -1830,7 +1858,7 @@ proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType =
|
||||
## returned. Otherwise ``typ`` is simply returned as-is.
|
||||
result = typ
|
||||
if typ.kind != kind:
|
||||
result = newType(kind, nextId(idgen), typ.owner)
|
||||
result = newType(kind, nextTypeId(idgen), typ.owner)
|
||||
rawAddSon(result, typ)
|
||||
|
||||
proc toRef*(typ: PType; idgen: IdGenerator): PType =
|
||||
@@ -1838,7 +1866,7 @@ proc toRef*(typ: PType; idgen: IdGenerator): PType =
|
||||
## returned. Otherwise ``typ`` is simply returned as-is.
|
||||
result = typ
|
||||
if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
|
||||
result = newType(tyRef, nextId(idgen), typ.owner)
|
||||
result = newType(tyRef, nextTypeId(idgen), typ.owner)
|
||||
rawAddSon(result, typ)
|
||||
|
||||
proc toObject*(typ: PType): PType =
|
||||
@@ -1886,10 +1914,8 @@ when false:
|
||||
for i in 0..<n.safeLen:
|
||||
if n[i].containsNil: return true
|
||||
|
||||
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
|
||||
|
||||
proc hasDisabledAsgn*(t: PType): bool =
|
||||
t.attachedOps[attachedAsgn] != nil and sfError in t.attachedOps[attachedAsgn].flags
|
||||
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
|
||||
|
||||
template incompleteType*(t: PType): bool =
|
||||
t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
|
||||
@@ -1897,8 +1923,6 @@ template incompleteType*(t: PType): bool =
|
||||
template typeCompleted*(s: PSym) =
|
||||
incl s.flags, sfNoForward
|
||||
|
||||
template getBody*(s: PSym): PNode = s.ast[bodyPos]
|
||||
|
||||
template detailedInfo*(sym: PSym): string =
|
||||
sym.name.s
|
||||
|
||||
@@ -1931,10 +1955,6 @@ proc addParam*(procType: PType; param: PSym) =
|
||||
procType.n.add newSymNode(param)
|
||||
rawAddSon(procType, param.typ)
|
||||
|
||||
template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
|
||||
template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
|
||||
template asink*(t: PType): PSym = t.attachedOps[attachedSink]
|
||||
|
||||
const magicsThatCanRaise = {
|
||||
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}
|
||||
|
||||
@@ -1952,9 +1972,13 @@ proc canRaise*(fn: PNode): bool =
|
||||
elif fn.kind == nkSym and fn.sym.magic == mEcho:
|
||||
result = true
|
||||
else:
|
||||
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
# TODO check for n having sons? or just return false for now if not
|
||||
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
|
||||
result = false
|
||||
else:
|
||||
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
|
||||
proc toHumanStrImpl[T](kind: T, num: static int): string =
|
||||
result = $kind
|
||||
@@ -1971,3 +1995,7 @@ proc toHumanStr*(kind: TTypeKind): string =
|
||||
|
||||
proc skipAddr*(n: PNode): PNode {.inline.} =
|
||||
(if n.kind == nkHiddenAddr: n[0] else: n)
|
||||
|
||||
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
|
||||
assert n.kind == nkTypeClassTy
|
||||
result = n[0].kind == nkEmpty
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the canonalization for the various caching mechanisms.
|
||||
|
||||
import strutils, db_sqlite, md5
|
||||
|
||||
var db: DbConn
|
||||
|
||||
# We *hash* the relevant information into 128 bit hashes. This should be good
|
||||
# enough to prevent any collisions.
|
||||
|
||||
type
|
||||
TUid = distinct MD5Digest
|
||||
|
||||
# For name mangling we encode these hashes via a variant of base64 (called
|
||||
# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
|
||||
# So a signature like:
|
||||
#
|
||||
# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
|
||||
#
|
||||
# is mangled into:
|
||||
# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
|
||||
#
|
||||
# This is a good compromise between correctness and brevity. ;-)
|
||||
|
||||
const
|
||||
cb64 = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
|
||||
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"_A", "_B"]
|
||||
|
||||
proc toBase64a(s: cstring, len: int): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||
var i = 0
|
||||
while i < s.len - 2:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
let c = ord(s[i+2])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result.add cb64[c and 0x3F]
|
||||
inc(i, 3)
|
||||
if i < s.len-1:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result.add cb64[((b and 0x0F) shl 2)]
|
||||
elif i < s.len:
|
||||
let a = ord(s[i])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[(a and 3) shl 4]
|
||||
|
||||
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
||||
|
||||
proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
|
||||
|
||||
proc hashSym(c: var MD5Context, s: PSym) =
|
||||
if sfAnon in s.flags or s.kind == skGenericParam:
|
||||
c &= ":anon"
|
||||
else:
|
||||
var it = s.owner
|
||||
while it != nil:
|
||||
hashSym(c, it)
|
||||
c &= "."
|
||||
it = s.owner
|
||||
c &= s.name.s
|
||||
|
||||
proc hashTree(c: var MD5Context, n: PNode) =
|
||||
if n == nil:
|
||||
c &= "\255"
|
||||
return
|
||||
var k = n.kind
|
||||
md5Update(c, cast[cstring](addr(k)), 1)
|
||||
# we really must not hash line information. 'n.typ' is debatable but
|
||||
# shouldn't be necessary for now and avoids potential infinite recursions.
|
||||
case n.kind
|
||||
of nkEmpty, nkNilLit, nkType: discard
|
||||
of nkIdent:
|
||||
c &= n.ident.s
|
||||
of nkSym:
|
||||
hashSym(c, n.sym)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
var v = n.intVal
|
||||
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
var v = n.floatVal
|
||||
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
c &= n.strVal
|
||||
else:
|
||||
for i in 0..<n.len: hashTree(c, n[i])
|
||||
|
||||
proc hashType(c: var MD5Context, t: PType) =
|
||||
# modelled after 'typeToString'
|
||||
if t == nil:
|
||||
c &= "\254"
|
||||
return
|
||||
|
||||
var k = t.kind
|
||||
md5Update(c, cast[cstring](addr(k)), 1)
|
||||
|
||||
if t.sym != nil and sfAnon notin t.sym.flags:
|
||||
# t.n for literals, but not for e.g. objects!
|
||||
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
||||
c.hashSym(t.sym)
|
||||
|
||||
case t.kind
|
||||
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
||||
for i in 0..<t.len-ord(t.kind != tyGenericInvocation):
|
||||
c.hashType t[i]
|
||||
of tyUserTypeClass:
|
||||
internalAssert t.sym != nil and t.sym.owner != nil
|
||||
c &= t.sym.owner.name.s
|
||||
of tyUserTypeClassInst:
|
||||
let body = t.base
|
||||
c.hashSym body.sym
|
||||
for i in 1..<t.len-1:
|
||||
c.hashType t[i]
|
||||
of tyFromExpr:
|
||||
c.hashTree(t.n)
|
||||
of tyArray:
|
||||
c.hashTree(t[0].n)
|
||||
c.hashType(t[1])
|
||||
of tyTuple:
|
||||
if t.n != nil:
|
||||
assert(t.n.len == t.len)
|
||||
for i in 0..<t.n.len:
|
||||
assert(t.n[i].kind == nkSym)
|
||||
c &= t.n[i].sym.name.s
|
||||
c &= ":"
|
||||
c.hashType(t[i])
|
||||
c &= ","
|
||||
else:
|
||||
for i in 0..<t.len: c.hashType t[i]
|
||||
of tyRange:
|
||||
c.hashTree(t.n)
|
||||
c.hashType(t[0])
|
||||
of tyProc:
|
||||
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
|
||||
for i in 0..<t.len: c.hashType(t[i])
|
||||
md5Update(c, cast[cstring](addr(t.callConv)), 1)
|
||||
|
||||
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
|
||||
if tfThread in t.flags: c &= ".thread"
|
||||
else:
|
||||
for i in 0..<t.len: c.hashType(t[i])
|
||||
if tfNotNil in t.flags: c &= "not nil"
|
||||
|
||||
proc canonConst(n: PNode): TUid =
|
||||
var c: MD5Context
|
||||
md5Init(c)
|
||||
c.hashTree(n)
|
||||
c.hashType(n.typ)
|
||||
md5Final(c, MD5Digest(result))
|
||||
|
||||
proc canonSym(s: PSym): TUid =
|
||||
var c: MD5Context
|
||||
md5Init(c)
|
||||
c.hashSym(s)
|
||||
md5Final(c, MD5Digest(result))
|
||||
|
||||
proc pushType(w: PRodWriter, t: PType) =
|
||||
# check so that the stack does not grow too large:
|
||||
if iiTableGet(w.index.tab, t.id) == InvalidKey:
|
||||
w.tstack.add(t)
|
||||
|
||||
proc pushSym(w: PRodWriter, s: PSym) =
|
||||
# check so that the stack does not grow too large:
|
||||
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
||||
w.sstack.add(s)
|
||||
|
||||
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
result: var string) =
|
||||
if n == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("()")
|
||||
return
|
||||
result.add('(')
|
||||
encodeVInt(ord(n.kind), result)
|
||||
# we do not write comments for now
|
||||
# Line information takes easily 20% or more of the filesize! Therefore we
|
||||
# omit line information if it is the same as the father's line information:
|
||||
if fInfo.fileIndex != n.info.fileIndex:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(n.info.line, result)
|
||||
result.add(',')
|
||||
encodeVInt(fileIdx(w, toFilename(n.info)), result)
|
||||
elif fInfo.line != n.info.line:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(n.info.line, result)
|
||||
elif fInfo.col != n.info.col:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
var f = n.flags * PersistentNodeFlags
|
||||
if f != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](f), result)
|
||||
if n.typ != nil:
|
||||
result.add('^')
|
||||
encodeVInt(n.typ.id, result)
|
||||
pushType(w, n.typ)
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
if n.intVal != 0:
|
||||
result.add('!')
|
||||
encodeVBiggestInt(n.intVal, result)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if n.floatVal != 0.0:
|
||||
result.add('!')
|
||||
encodeStr($n.floatVal, result)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if n.strVal != "":
|
||||
result.add('!')
|
||||
encodeStr(n.strVal, result)
|
||||
of nkIdent:
|
||||
result.add('!')
|
||||
encodeStr(n.ident.s, result)
|
||||
of nkSym:
|
||||
result.add('!')
|
||||
encodeVInt(n.sym.id, result)
|
||||
pushSym(w, n.sym)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
encodeNode(w, n.info, n[i], result)
|
||||
result.add(')')
|
||||
|
||||
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||
var oldLen = result.len
|
||||
result.add('<')
|
||||
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
|
||||
if loc.s != low(loc.s):
|
||||
result.add('*')
|
||||
encodeVInt(ord(loc.s), result)
|
||||
if loc.flags != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](loc.flags), result)
|
||||
if loc.t != nil:
|
||||
result.add('^')
|
||||
encodeVInt(cast[int32](loc.t.id), result)
|
||||
pushType(w, loc.t)
|
||||
if loc.r != nil:
|
||||
result.add('!')
|
||||
encodeStr($loc.r, result)
|
||||
if loc.a != 0:
|
||||
result.add('?')
|
||||
encodeVInt(loc.a, result)
|
||||
if oldLen + 1 == result.len:
|
||||
# no data was necessary, so remove the '<' again:
|
||||
setLen(result, oldLen)
|
||||
else:
|
||||
result.add('>')
|
||||
|
||||
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||
if t == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("[]")
|
||||
return
|
||||
# we need no surrounding [] here because the type is in a line of its own
|
||||
if t.kind == tyForward: internalError("encodeType: tyForward")
|
||||
# for the new rodfile viewer we use a preceding [ so that the data section
|
||||
# can easily be disambiguated:
|
||||
result.add('[')
|
||||
encodeVInt(ord(t.kind), result)
|
||||
result.add('+')
|
||||
encodeVInt(t.id, result)
|
||||
if t.n != nil:
|
||||
encodeNode(w, unknownLineInfo, t.n, result)
|
||||
if t.flags != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](t.flags), result)
|
||||
if t.callConv != low(t.callConv):
|
||||
result.add('?')
|
||||
encodeVInt(ord(t.callConv), result)
|
||||
if t.owner != nil:
|
||||
result.add('*')
|
||||
encodeVInt(t.owner.id, result)
|
||||
pushSym(w, t.owner)
|
||||
if t.sym != nil:
|
||||
result.add('&')
|
||||
encodeVInt(t.sym.id, result)
|
||||
pushSym(w, t.sym)
|
||||
if t.size != - 1:
|
||||
result.add('/')
|
||||
encodeVBiggestInt(t.size, result)
|
||||
if t.align != - 1:
|
||||
result.add('=')
|
||||
encodeVInt(t.align, result)
|
||||
encodeLoc(w, t.loc, result)
|
||||
for i in 0..<t.len:
|
||||
if t[i] == nil:
|
||||
result.add("^()")
|
||||
else:
|
||||
result.add('^')
|
||||
encodeVInt(t[i].id, result)
|
||||
pushType(w, t[i])
|
||||
|
||||
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
||||
result.add('|')
|
||||
encodeVInt(ord(lib.kind), result)
|
||||
result.add('|')
|
||||
encodeStr($lib.name, result)
|
||||
result.add('|')
|
||||
encodeNode(w, info, lib.path, result)
|
||||
|
||||
proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
|
||||
if s == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("{}")
|
||||
return
|
||||
# we need no surrounding {} here because the symbol is in a line of its own
|
||||
encodeVInt(ord(s.kind), result)
|
||||
result.add('+')
|
||||
encodeVInt(s.id, result)
|
||||
result.add('&')
|
||||
encodeStr(s.name.s, result)
|
||||
if s.typ != nil:
|
||||
result.add('^')
|
||||
encodeVInt(s.typ.id, result)
|
||||
pushType(w, s.typ)
|
||||
result.add('?')
|
||||
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
|
||||
result.add(',')
|
||||
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
|
||||
result.add(',')
|
||||
encodeVInt(fileIdx(w, toFilename(s.info)), result)
|
||||
if s.owner != nil:
|
||||
result.add('*')
|
||||
encodeVInt(s.owner.id, result)
|
||||
pushSym(w, s.owner)
|
||||
if s.flags != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](s.flags), result)
|
||||
if s.magic != mNone:
|
||||
result.add('@')
|
||||
encodeVInt(ord(s.magic), result)
|
||||
if s.options != w.options:
|
||||
result.add('!')
|
||||
encodeVInt(cast[int32](s.options), result)
|
||||
if s.position != 0:
|
||||
result.add('%')
|
||||
encodeVInt(s.position, result)
|
||||
if s.offset != - 1:
|
||||
result.add('`')
|
||||
encodeVInt(s.offset, result)
|
||||
encodeLoc(w, s.loc, result)
|
||||
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
|
||||
if s.constraint != nil:
|
||||
result.add('#')
|
||||
encodeNode(w, unknownLineInfo, s.constraint, result)
|
||||
# lazy loading will soon reload the ast lazily, so the ast needs to be
|
||||
# the last entry of a symbol:
|
||||
if s.ast != nil:
|
||||
# we used to attempt to save space here by only storing a dummy AST if
|
||||
# it is not necessary, but Nim's heavy compile-time evaluation features
|
||||
# make that unfeasible nowadays:
|
||||
encodeNode(w, s.info, s.ast, result)
|
||||
|
||||
|
||||
proc createDb() =
|
||||
db.exec(sql"""
|
||||
create table if not exists Module(
|
||||
id integer primary key,
|
||||
name varchar(256) not null,
|
||||
fullpath varchar(256) not null,
|
||||
interfHash varchar(256) not null,
|
||||
fullHash varchar(256) not null,
|
||||
|
||||
created timestamp not null default (DATETIME('now'))
|
||||
);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists Backend(
|
||||
id integer primary key,
|
||||
strongdeps varchar(max) not null,
|
||||
weakdeps varchar(max) not null,
|
||||
header varchar(max) not null,
|
||||
code varchar(max) not null
|
||||
)
|
||||
|
||||
create table if not exists Symbol(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
backend integer not null,
|
||||
name varchar(max) not null,
|
||||
data varchar(max) not null,
|
||||
created timestamp not null default (DATETIME('now')),
|
||||
|
||||
foreign key (module) references Module(id),
|
||||
foreign key (backend) references Backend(id)
|
||||
);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists Type(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
name varchar(max) not null,
|
||||
data varchar(max) not null,
|
||||
created timestamp not null default (DATETIME('now')),
|
||||
|
||||
foreign key (module) references module(id)
|
||||
);""")
|
||||
|
||||
|
||||
@@ -289,6 +289,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rop
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||
#assert result != nil
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope =
|
||||
var a: TLoc
|
||||
@@ -298,11 +299,11 @@ proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope =
|
||||
initLocExprSingleUse(p, n, a)
|
||||
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||
|
||||
from dfa import instrTargets, InstrTargetKind
|
||||
from dfa import aliases, AliasKind
|
||||
|
||||
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
|
||||
for p in potentialWrites:
|
||||
if instrTargets(p, n) != None:
|
||||
if p.aliases(n) != no or n.aliases(p) != no:
|
||||
return true
|
||||
|
||||
proc skipTrivialIndirections(n: PNode): PNode =
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
when defined(nimCompilerStackraceHints):
|
||||
import std/stackframes
|
||||
|
||||
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
|
||||
result: var Rope; count: var int;
|
||||
isConst: bool, info: TLineInfo)
|
||||
@@ -82,10 +85,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
of tyNil:
|
||||
result = genNilStringLiteral(p.module, n.info)
|
||||
of tyString:
|
||||
# with the new semantics for 'nil' strings, we can map "" to nil and
|
||||
# with the new semantics for not 'nil' strings, we can map "" to nil and
|
||||
# save tons of allocations:
|
||||
if n.strVal.len == 0 and optNilSeqs notin p.options and
|
||||
optSeqDestructors notin p.config.globalOptions:
|
||||
if n.strVal.len == 0 and optSeqDestructors notin p.config.globalOptions:
|
||||
result = genNilStringLiteral(p.module, n.info)
|
||||
else:
|
||||
result = genStringLiteral(p.module, n)
|
||||
@@ -173,8 +175,7 @@ proc canMove(p: BProc, n: PNode; dest: TLoc): bool =
|
||||
if not isDeepConstExpr(n) or n.len == 0:
|
||||
if skipTypes(n.typ, abstractVarRange).kind == tySequence:
|
||||
return true
|
||||
elif optNilSeqs notin p.options and
|
||||
n.kind in nkStrKinds and n.strVal.len == 0:
|
||||
elif n.kind in nkStrKinds and n.strVal.len == 0:
|
||||
# Empty strings are codegen'd as NIM_NIL so it's just a pointer copy
|
||||
return true
|
||||
result = n.kind in nkCallKinds
|
||||
@@ -994,14 +995,9 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
if ty.kind in {tyRef, tyPtr}:
|
||||
ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
|
||||
if optBoundsCheck in p.options:
|
||||
if ty.kind == tyString and not defined(nimNoZeroTerminator):
|
||||
linefmt(p, cpsStmts,
|
||||
"if ((NU)($1) > (NU)$2){ #raiseIndexError2($1,$2); $3}$n",
|
||||
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||
else:
|
||||
linefmt(p, cpsStmts,
|
||||
"if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n",
|
||||
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||
linefmt(p, cpsStmts,
|
||||
"if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n",
|
||||
[rdLoc(b), lenExpr(p, a), raiseInstr(p)])
|
||||
if d.k == locNone: d.storage = OnHeap
|
||||
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
||||
a.r = ropecg(p.module, "(*$1)", [a.r])
|
||||
@@ -1289,16 +1285,17 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
|
||||
genAssignment(p, a, b, {})
|
||||
else:
|
||||
let ti = genTypeInfoV1(p.module, typ, a.lode.info)
|
||||
if bt.destructor != nil and not isTrivialProc(bt.destructor):
|
||||
let op = getAttachedOp(p.module.g.graph, bt, attachedDestructor)
|
||||
if op != nil and not isTrivialProc(p.module.g.graph, op):
|
||||
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
||||
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
||||
# convention at least:
|
||||
if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccNimCall:
|
||||
if op.typ == nil or op.typ.callConv != ccNimCall:
|
||||
localError(p.module.config, a.lode.info,
|
||||
"the destructor that is turned into a finalizer needs " &
|
||||
"to have the 'nimcall' calling convention")
|
||||
var f: TLoc
|
||||
initLocExpr(p, newSymNode(bt.destructor), f)
|
||||
initLocExpr(p, newSymNode(op), f)
|
||||
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
|
||||
|
||||
if a.storage == OnHeap and usesWriteBarrier(p.config):
|
||||
@@ -1372,8 +1369,7 @@ proc genNewSeq(p: BProc, e: PNode) =
|
||||
getTypeDesc(p.module, seqtype.lastSon),
|
||||
getSeqPayloadType(p.module, seqtype)])
|
||||
else:
|
||||
let lenIsZero = optNilSeqs notin p.options and
|
||||
e[2].kind == nkIntLit and e[2].intVal == 0
|
||||
let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0
|
||||
genNewSeqAux(p, a, b.rdLoc, lenIsZero)
|
||||
gcUsage(p.config, e)
|
||||
|
||||
@@ -1498,8 +1494,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
getSeqPayloadType(p.module, seqtype)])
|
||||
else:
|
||||
# generate call to newSeq before adding the elements per hand:
|
||||
genNewSeqAux(p, dest[], l,
|
||||
optNilSeqs notin p.options and n.len == 0)
|
||||
genNewSeqAux(p, dest[], l, n.len == 0)
|
||||
for i in 0..<n.len:
|
||||
initLoc(arr, locExpr, n[i], OnHeap)
|
||||
arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), intLiteral(i), dataField(p)])
|
||||
@@ -1528,7 +1523,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
|
||||
[rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon),
|
||||
getSeqPayloadType(p.module, seqtype)])
|
||||
else:
|
||||
genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0)
|
||||
genNewSeqAux(p, d, intLiteral(L), L == 0)
|
||||
initLocExpr(p, n[1], a)
|
||||
# bug #5007; do not produce excessive C source code:
|
||||
if L < 10:
|
||||
@@ -1769,7 +1764,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else: internalError(p.config, e.info, "genArrayLen()")
|
||||
|
||||
proc makePtrType(baseType: PType; idgen: IdGenerator): PType =
|
||||
result = newType(tyPtr, nextId idgen, baseType.owner)
|
||||
result = newType(tyPtr, nextTypeId idgen, baseType.owner)
|
||||
addSonSkipIntLit(result, baseType, idgen)
|
||||
|
||||
proc makeAddr(n: PNode; idgen: IdGenerator): PNode =
|
||||
@@ -2201,7 +2196,8 @@ proc genDestroy(p: BProc; n: PNode) =
|
||||
else: discard "nothing to do"
|
||||
else:
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
if t.destructor != nil and t.destructor.ast[bodyPos].len != 0:
|
||||
let op = getAttachedOp(p.module.g.graph, t, attachedDestructor)
|
||||
if op != nil and getBody(p.module.g.graph, op).len != 0:
|
||||
internalError(p.config, n.info, "destructor turned out to be not trivial")
|
||||
discard "ignore calls to the default destructor"
|
||||
|
||||
@@ -2231,16 +2227,9 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) =
|
||||
"'toOpenArray' is only valid within a call expression")
|
||||
|
||||
proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
|
||||
const ToStringProcSlot = -4
|
||||
let t = e[1].typ.skipTypes(abstractInst+{tyRange})
|
||||
var toStrProc: PSym = nil
|
||||
for idx, p in items(t.methods):
|
||||
if idx == ToStringProcSlot:
|
||||
toStrProc = p
|
||||
break
|
||||
if toStrProc == nil:
|
||||
toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph, p.module.idgen)
|
||||
t.methods.add((ToStringProcSlot, toStrProc))
|
||||
let toStrProc = getToStringProc(p.module.g.graph, t)
|
||||
# XXX need to modify this logic for IC.
|
||||
var n = copyTree(e)
|
||||
n[0] = newSymNode(toStrProc)
|
||||
expr(p, n, d)
|
||||
@@ -2307,7 +2296,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
|
||||
of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
|
||||
of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
|
||||
of mStrToStr, mUnown, mIsolate: expr(p, e[1], d)
|
||||
of mStrToStr, mUnown: expr(p, e[1], d)
|
||||
of mIsolate: genCall(p, e, d)
|
||||
of mEnumToStr:
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
genEnumToStr(p, e, d)
|
||||
@@ -2364,6 +2354,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
# somehow forward-declared from some other usage, but it is *possible*
|
||||
if lfNoDecl notin opr.loc.flags:
|
||||
let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r)
|
||||
assert prc != nil, $opr.loc.r
|
||||
# HACK:
|
||||
# Explicitly add this proc as declared here so the cgsym call doesn't
|
||||
# add a forward declaration - without this we could end up with the same
|
||||
@@ -2586,40 +2577,32 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
[getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
|
||||
|
||||
proc downConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
if p.module.compileToCpp:
|
||||
discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs))
|
||||
expr(p, n[0], d) # downcast does C++ for us
|
||||
else:
|
||||
var dest = skipTypes(n.typ, abstractPtrs)
|
||||
var arg = n[0]
|
||||
while arg.kind == nkObjDownConv: arg = arg[0]
|
||||
|
||||
var arg = n[0]
|
||||
while arg.kind == nkObjDownConv: arg = arg[0]
|
||||
|
||||
var src = skipTypes(arg.typ, abstractPtrs)
|
||||
discard getTypeDesc(p.module, src)
|
||||
let dest = skipTypes(n.typ, abstractPtrs)
|
||||
let src = skipTypes(arg.typ, abstractPtrs)
|
||||
discard getTypeDesc(p.module, src)
|
||||
let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}
|
||||
if isRef and d.k == locNone and n.typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr} and n.isLValue:
|
||||
# it can happen that we end up generating '&&x->Sup' here, so we pack
|
||||
# the '&x->Sup' into a temporary and then those address is taken
|
||||
# (see bug #837). However sometimes using a temporary is not correct:
|
||||
# init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
|
||||
# this by ensuring the destination is also a pointer:
|
||||
var a: TLoc
|
||||
initLocExpr(p, arg, a)
|
||||
var r = rdLoc(a)
|
||||
let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}
|
||||
if isRef:
|
||||
r.add("->Sup")
|
||||
else:
|
||||
r.add(".Sup")
|
||||
putIntoDest(p, d, n,
|
||||
"(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
elif p.module.compileToCpp:
|
||||
# C++ implicitly downcasts for us
|
||||
expr(p, arg, d)
|
||||
else:
|
||||
var a: TLoc
|
||||
initLocExpr(p, arg, a)
|
||||
var r = rdLoc(a) & (if isRef: "->Sup" else: ".Sup")
|
||||
for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup")
|
||||
if isRef:
|
||||
# it can happen that we end up generating '&&x->Sup' here, so we pack
|
||||
# the '&x->Sup' into a temporary and then those address is taken
|
||||
# (see bug #837). However sometimes using a temporary is not correct:
|
||||
# init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
|
||||
# this by ensuring the destination is also a pointer:
|
||||
if d.k == locNone and skipTypes(n.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}:
|
||||
getTemp(p, n.typ, d)
|
||||
linefmt(p, cpsStmts, "$1 = &$2;$n", [rdLoc(d), r])
|
||||
else:
|
||||
r = "&" & r
|
||||
putIntoDest(p, d, n, r, a.storage)
|
||||
else:
|
||||
putIntoDest(p, d, n, r, a.storage)
|
||||
putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage)
|
||||
|
||||
proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
|
||||
let t = n.typ
|
||||
@@ -2642,7 +2625,60 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
|
||||
if t.kind notin {tySequence, tyString}:
|
||||
d.storage = OnStatic
|
||||
|
||||
proc genConstSetup(p: BProc; sym: PSym): bool =
|
||||
let m = p.module
|
||||
useHeader(m, sym)
|
||||
if sym.loc.k == locNone:
|
||||
fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
|
||||
if m.hcrOn: incl(sym.loc.flags, lfIndirect)
|
||||
result = lfNoDecl notin sym.loc.flags
|
||||
|
||||
proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
|
||||
assert(sym.loc.r != nil)
|
||||
if m.hcrOn:
|
||||
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
|
||||
m.initProc.procSec(cpsLocals).addf(
|
||||
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
||||
getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
|
||||
else:
|
||||
let headerDecl = "extern NIM_CONST $1 $2;$n" %
|
||||
[getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
|
||||
m.s[cfsData].add(headerDecl)
|
||||
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
|
||||
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
|
||||
|
||||
proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
|
||||
# add a suffix for hcr - will later init the global pointer with this data
|
||||
let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r
|
||||
q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n",
|
||||
[getTypeDesc(q, sym.typ), actualConstName,
|
||||
genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)])
|
||||
if q.hcrOn:
|
||||
# generate the global pointer with the real name
|
||||
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r])
|
||||
# register it (but ignore the boolean result of hcrRegisterGlobal)
|
||||
q.initProc.procSec(cpsLocals).addf(
|
||||
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
|
||||
[getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
|
||||
# always copy over the contents of the actual constant with the _const
|
||||
# suffix ==> this means that the constant is reloadable & updatable!
|
||||
q.initProc.procSec(cpsLocals).add(ropecg(q,
|
||||
"\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
|
||||
[sym.loc.r, actualConstName, rdLoc(sym.loc)]))
|
||||
|
||||
proc genConstStmt(p: BProc, n: PNode) =
|
||||
# This code is only used in the new DCE implementation.
|
||||
assert useAliveDataFromDce in p.module.flags
|
||||
let m = p.module
|
||||
for it in n:
|
||||
if it[0].kind == nkSym:
|
||||
let sym = it[0].sym
|
||||
if not isSimpleConst(sym.typ) and sym.itemId.item in m.alive and genConstSetup(p, sym):
|
||||
genConstDefinition(m, p, sym)
|
||||
|
||||
proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
when defined(nimCompilerStackraceHints):
|
||||
setFrameMsg p.config$n.info & " " & $n.kind
|
||||
p.currLineInfo = n.info
|
||||
|
||||
case n.kind
|
||||
@@ -2650,7 +2686,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
var sym = n.sym
|
||||
case sym.kind
|
||||
of skMethod:
|
||||
if {sfDispatcher, sfForward} * sym.flags != {}:
|
||||
if useAliveDataFromDce in p.module.flags or {sfDispatcher, sfForward} * sym.flags != {}:
|
||||
# we cannot produce code for the dispatcher yet:
|
||||
fillProcLoc(p.module, n)
|
||||
genProcPrototype(p.module, sym)
|
||||
@@ -2663,13 +2699,21 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if sfCompileTime in sym.flags:
|
||||
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
|
||||
sym.name.s)
|
||||
genProc(p.module, sym)
|
||||
if useAliveDataFromDce in p.module.flags and sym.typ.callConv != ccInline:
|
||||
fillProcLoc(p.module, n)
|
||||
genProcPrototype(p.module, sym)
|
||||
else:
|
||||
genProc(p.module, sym)
|
||||
if sym.loc.r == nil or sym.loc.lode == nil:
|
||||
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
of skConst:
|
||||
if isSimpleConst(sym.typ):
|
||||
putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
|
||||
elif useAliveDataFromDce in p.module.flags:
|
||||
genConstHeader(p.module, p.module, p, sym)
|
||||
assert((sym.loc.r != nil) and (sym.loc.t != nil))
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
else:
|
||||
genComplexConst(p, sym, d)
|
||||
of skEnumField:
|
||||
@@ -2790,7 +2834,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkEmpty: discard
|
||||
of nkWhileStmt: genWhileStmt(p, n)
|
||||
of nkVarSection, nkLetSection: genVarStmt(p, n)
|
||||
of nkConstSection: discard # consts generated lazily on use
|
||||
of nkConstSection:
|
||||
if useAliveDataFromDce in p.module.flags:
|
||||
genConstStmt(p, n)
|
||||
# else: consts generated lazily on use
|
||||
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
|
||||
of nkCaseStmt: genCase(p, n, d)
|
||||
of nkReturnStmt: genReturnStmt(p, n)
|
||||
@@ -2835,13 +2882,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
|
||||
if n[genericParamsPos].kind == nkEmpty:
|
||||
var prc = n[namePos].sym
|
||||
# due to a bug/limitation in the lambda lifting, unused inner procs
|
||||
# are not transformed correctly. We work around this issue (#411) here
|
||||
# by ensuring it's no inner proc (owner is a module):
|
||||
if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
|
||||
if useAliveDataFromDce in p.module.flags:
|
||||
if p.module.alive.contains(prc.itemId.item) and prc.magic in {mNone, mIsolate}:
|
||||
genProc(p.module, prc)
|
||||
elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
|
||||
if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
|
||||
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
|
||||
(prc.kind == skMethod):
|
||||
# due to a bug/limitation in the lambda lifting, unused inner procs
|
||||
# are not transformed correctly. We work around this issue (#411) here
|
||||
# by ensuring it's no inner proc (owner is a module).
|
||||
# Generate proc even if empty body, bugfix #11651.
|
||||
genProc(p.module, prc)
|
||||
of nkParForStmt: genParForStmt(p, n)
|
||||
@@ -2851,6 +2901,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
inc p.splitDecls
|
||||
genGotoState(p, n)
|
||||
of nkBreakState: genBreakState(p, n, d)
|
||||
of nkMixinStmt, nkBindStmt: discard
|
||||
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||
|
||||
proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope =
|
||||
@@ -3089,7 +3140,9 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope
|
||||
else:
|
||||
result = genConstSeq(p, n, typ, isConst)
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit:
|
||||
if typ.callConv == ccClosure and n.safeLen > 1 and n[1].kind == nkNilLit:
|
||||
# n.kind could be: nkClosure, nkTupleConstr and maybe others; `n.safeLen`
|
||||
# guards against the case of `nkSym`, refs bug #14340.
|
||||
# Conversion: nimcall -> closure.
|
||||
# this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
|
||||
# this behaviour is needed since closure_var = nil must be
|
||||
|
||||
@@ -218,9 +218,6 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
|
||||
else: doAssert(false, "ccgmerge: unknown key: " & k)
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template withCFile(cfilename: AbsoluteFile, body: untyped) =
|
||||
var s = llStreamOpen(cfilename, fmRead)
|
||||
if s == nil: return
|
||||
@@ -25,10 +25,10 @@ proc getTraverseProc(p: BProc, v: PSym): Rope =
|
||||
|
||||
proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) =
|
||||
if sfThread in v.flags:
|
||||
appcg(p.module, p.module.initProc.procSec(cpsInit),
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
|
||||
else:
|
||||
appcg(p.module, p.module.initProc.procSec(cpsInit),
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
|
||||
|
||||
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
|
||||
@@ -653,12 +653,19 @@ proc genParForStmt(p: BProc, t: PNode) =
|
||||
initLocExpr(p, call[2], rangeB)
|
||||
|
||||
# $n at the beginning because of #9710
|
||||
if call.len == 4: # `||`(a, b, annotation)
|
||||
lineF(p, cpsStmts, "$n#pragma omp $4$n" &
|
||||
"for ($1 = $2; $1 <= $3; ++$1)",
|
||||
[forLoopVar.loc.rdLoc,
|
||||
rangeA.rdLoc, rangeB.rdLoc,
|
||||
call[3].getStr.rope])
|
||||
if call.len == 4: # procName(a, b, annotation)
|
||||
if call[0].sym.name.s == "||": # `||`(a, b, annotation)
|
||||
lineF(p, cpsStmts, "$n#pragma omp $4$n" &
|
||||
"for ($1 = $2; $1 <= $3; ++$1)",
|
||||
[forLoopVar.loc.rdLoc,
|
||||
rangeA.rdLoc, rangeB.rdLoc,
|
||||
call[3].getStr.rope])
|
||||
else:
|
||||
lineF(p, cpsStmts, "$n#pragma $4$n" &
|
||||
"for ($1 = $2; $1 <= $3; ++$1)",
|
||||
[forLoopVar.loc.rdLoc,
|
||||
rangeA.rdLoc, rangeB.rdLoc,
|
||||
call[3].getStr.rope])
|
||||
else: # `||`(a, b, step, annotation)
|
||||
var step: TLoc
|
||||
initLocExpr(p, call[3], step)
|
||||
@@ -1523,20 +1530,21 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
|
||||
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
|
||||
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)])
|
||||
|
||||
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
|
||||
const ObjDiscMappingProcSlot = -5
|
||||
var theProc: PSym = nil
|
||||
for idx, p in items(t.methods):
|
||||
if idx == ObjDiscMappingProcSlot:
|
||||
theProc = p
|
||||
break
|
||||
if theProc == nil:
|
||||
theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
|
||||
t.methods.add((ObjDiscMappingProcSlot, theProc))
|
||||
var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
|
||||
call.add newSymNode(theProc)
|
||||
call.add e
|
||||
expr(p, call, d)
|
||||
when false:
|
||||
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
|
||||
const ObjDiscMappingProcSlot = -5
|
||||
var theProc: PSym = nil
|
||||
for idx, p in items(t.methods):
|
||||
if idx == ObjDiscMappingProcSlot:
|
||||
theProc = p
|
||||
break
|
||||
if theProc == nil:
|
||||
theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
|
||||
t.methods.add((ObjDiscMappingProcSlot, theProc))
|
||||
var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
|
||||
call.add newSymNode(theProc)
|
||||
call.add e
|
||||
expr(p, call, d)
|
||||
|
||||
proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||
var a, tmp: TLoc
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Thread var support for crappy architectures that lack native support for
|
||||
## thread local storage. (**Thank you Mac OS X!**)
|
||||
## Thread var support for architectures that lack native support for
|
||||
## thread local storage.
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
@@ -35,7 +35,11 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
if isExtern: m.s[cfsVars].add("extern ")
|
||||
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
|
||||
else: m.s[cfsVars].add("N_LIB_PRIVATE ")
|
||||
if optThreads in m.config.globalOptions: m.s[cfsVars].add("NIM_THREADVAR ")
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
|
||||
else: m.s[cfsVars].add("NIM_THREADVAR ")
|
||||
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
|
||||
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
|
||||
|
||||
|
||||
@@ -36,19 +36,17 @@ proc mangleField(m: BModule; name: PIdent): string =
|
||||
if isKeyword(name):
|
||||
result.add "_0"
|
||||
|
||||
when false:
|
||||
proc hashOwner(s: PSym): SigHash =
|
||||
var m = s
|
||||
while m.kind != skModule: m = m.owner
|
||||
let p = m.owner
|
||||
assert p.kind == skPackage
|
||||
result = gDebugInfo.register(p.name.s, m.name.s)
|
||||
|
||||
proc mangleName(m: BModule; s: PSym): Rope =
|
||||
result = s.loc.r
|
||||
if result == nil:
|
||||
result = s.name.s.mangle.rope
|
||||
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts))
|
||||
result.add "_"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_"
|
||||
result.add rope s.itemId.item
|
||||
if m.hcrOn:
|
||||
result.add "_"
|
||||
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts))
|
||||
s.loc.r = result
|
||||
writeMangledName(m.ndi, s, m.config)
|
||||
|
||||
@@ -1267,9 +1265,9 @@ proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||
|
||||
proc fakeClosureType(m: BModule; owner: PSym): PType =
|
||||
# we generate the same RTTI as for a tuple[pointer, ref tuple[]]
|
||||
result = newType(tyTuple, nextId m.idgen, owner)
|
||||
result.rawAddSon(newType(tyPointer, nextId m.idgen, owner))
|
||||
var r = newType(tyRef, nextId m.idgen, owner)
|
||||
result = newType(tyTuple, nextTypeId m.idgen, owner)
|
||||
result.rawAddSon(newType(tyPointer, nextTypeId m.idgen, owner))
|
||||
var r = newType(tyRef, nextTypeId m.idgen, owner)
|
||||
let obj = createObj(m.g.graph, m.idgen, owner, owner.info, final=false)
|
||||
r.rawAddSon(obj)
|
||||
result.rawAddSon(r)
|
||||
@@ -1281,12 +1279,12 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
|
||||
m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
|
||||
[result, s.loc.r])
|
||||
|
||||
proc declareNimType(m: BModule, name: string; str: Rope, ownerModule: PSym) =
|
||||
proc declareNimType(m: BModule, name: string; str: Rope, module: int) =
|
||||
let nr = rope(name)
|
||||
if m.hcrOn:
|
||||
m.s[cfsData].addf("static $2* $1;$n", [str, nr])
|
||||
m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n",
|
||||
[str, getModuleDllPath(m, ownerModule), nr])
|
||||
[str, getModuleDllPath(m, module), nr])
|
||||
else:
|
||||
m.s[cfsData].addf("extern $2 $1;$n", [str, nr])
|
||||
|
||||
@@ -1313,11 +1311,11 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope =
|
||||
it = it[0]
|
||||
result = makeCString(res)
|
||||
|
||||
proc isTrivialProc(s: PSym): bool {.inline.} = s.ast[bodyPos].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): Rope =
|
||||
let theProc = t.attachedOps[op]
|
||||
if theProc != nil and not isTrivialProc(theProc):
|
||||
let theProc = getAttachedOp(m.g.graph, t, op)
|
||||
if theProc != nil and not isTrivialProc(m.g.graph, theProc):
|
||||
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
|
||||
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
|
||||
# convention at least:
|
||||
@@ -1338,7 +1336,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
|
||||
|
||||
proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
|
||||
var typeName: Rope
|
||||
if t.kind == tyObject:
|
||||
if t.kind in {tyObject, tyDistinct}:
|
||||
if incompleteType(t):
|
||||
localError(m.config, info, "request for RTTI generation for incomplete object: " &
|
||||
typeToString(t))
|
||||
@@ -1359,9 +1357,13 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
|
||||
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
|
||||
discard genTypeInfoV1(m, t, info)
|
||||
|
||||
proc moduleOpenForCodegen(m: BModule; module: int32): bool {.inline.} =
|
||||
result = module < m.g.modules.len and m.g.modules[module] != nil
|
||||
|
||||
proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
let origType = t
|
||||
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
|
||||
# distinct types can have their own destructors
|
||||
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct})
|
||||
|
||||
let prefixTI = if m.hcrOn: "(" else: "(&"
|
||||
|
||||
@@ -1381,11 +1383,11 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
result = "NTIv2$1_" % [rope($sig)]
|
||||
m.typeInfoMarkerV2[sig] = result
|
||||
|
||||
let owner = t.skipTypes(typedescPtrs).owner.getModule
|
||||
if owner != m.module:
|
||||
let owner = t.skipTypes(typedescPtrs).itemId.module
|
||||
if owner != m.module.position and moduleOpenForCodegen(m, owner):
|
||||
# make sure the type info is created in the owner module
|
||||
assert m.g.modules[owner.position] != nil
|
||||
discard genTypeInfoV2(m.g.modules[owner.position], origType, info)
|
||||
assert m.g.modules[owner] != nil
|
||||
discard genTypeInfoV2(m.g.modules[owner], origType, info)
|
||||
# reference the type info as extern here
|
||||
discard cgsym(m, "TNimTypeV2")
|
||||
declareNimType(m, "TNimTypeV2", result, owner)
|
||||
@@ -1396,14 +1398,41 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
result = prefixTI.rope & result & ")".rope
|
||||
|
||||
proc openArrayToTuple(m: BModule; t: PType): PType =
|
||||
result = newType(tyTuple, nextId m.idgen, t.owner)
|
||||
let p = newType(tyPtr, nextId m.idgen, t.owner)
|
||||
let a = newType(tyUncheckedArray, nextId m.idgen, t.owner)
|
||||
result = newType(tyTuple, nextTypeId m.idgen, t.owner)
|
||||
let p = newType(tyPtr, nextTypeId m.idgen, t.owner)
|
||||
let a = newType(tyUncheckedArray, nextTypeId m.idgen, t.owner)
|
||||
a.add t.lastSon
|
||||
p.add a
|
||||
result.add p
|
||||
result.add getSysType(m.g.graph, t.owner.info, tyInt)
|
||||
|
||||
proc typeToC(t: PType): string =
|
||||
## Just for more readable names, the result doesn't have
|
||||
## to be unique.
|
||||
let s = typeToString(t)
|
||||
result = newStringOfCap(s.len)
|
||||
for i in 0..<s.len:
|
||||
let c = s[i]
|
||||
case c
|
||||
of 'a'..'z':
|
||||
result.add c
|
||||
of 'A'..'Z':
|
||||
result.add toLowerAscii(c)
|
||||
of ' ':
|
||||
discard
|
||||
of ',':
|
||||
result.add '_'
|
||||
of '.':
|
||||
result.add 'O'
|
||||
of '[', '(', '{':
|
||||
result.add 'L'
|
||||
of ']', ')', '}':
|
||||
result.add 'T'
|
||||
else:
|
||||
# We mangle upper letters and digits too so that there cannot
|
||||
# be clashes with our special meanings
|
||||
result.addInt ord(c)
|
||||
|
||||
proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
let origType = t
|
||||
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
|
||||
@@ -1424,14 +1453,14 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
m.typeInfoMarker[sig] = marker.str
|
||||
return prefixTI.rope & marker.str & ")".rope
|
||||
|
||||
result = "NTI$1_" % [rope($sig)]
|
||||
result = "NTI$1$2_" % [rope(typeToC(t)), rope($sig)]
|
||||
m.typeInfoMarker[sig] = result
|
||||
|
||||
let owner = t.skipTypes(typedescPtrs).owner.getModule
|
||||
if owner != m.module:
|
||||
let owner = t.skipTypes(typedescPtrs).itemId.module
|
||||
if owner != m.module.position and moduleOpenForCodegen(m, owner):
|
||||
# make sure the type info is created in the owner module
|
||||
assert m.g.modules[owner.position] != nil
|
||||
discard genTypeInfoV1(m.g.modules[owner.position], origType, info)
|
||||
assert m.g.modules[owner] != nil
|
||||
discard genTypeInfoV1(m.g.modules[owner], origType, info)
|
||||
# reference the type info as extern here
|
||||
discard cgsym(m, "TNimType")
|
||||
discard cgsym(m, "TNimNode")
|
||||
@@ -1483,10 +1512,11 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
genTupleInfo(m, x, origType, result, info)
|
||||
else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
|
||||
|
||||
if t.attachedOps[attachedDeepCopy] != nil:
|
||||
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
|
||||
elif origType.attachedOps[attachedDeepCopy] != nil:
|
||||
genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result)
|
||||
var op = getAttachedOp(m.g.graph, t, attachedDeepCopy)
|
||||
if op == nil:
|
||||
op = getAttachedOp(m.g.graph, origType, attachedDeepCopy)
|
||||
if op != nil:
|
||||
genDeepCopyProc(m, op, result)
|
||||
|
||||
if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags:
|
||||
let v2info = genTypeInfoV2(m, origType, info)
|
||||
|
||||
@@ -13,8 +13,8 @@ import
|
||||
ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
|
||||
nversion, nimsets, msgs, bitsets, idents, types,
|
||||
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
|
||||
rodutils, renderer, cgendata, ccgmerge, aliases,
|
||||
lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr,
|
||||
rodutils, renderer, cgendata, aliases,
|
||||
lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
|
||||
injectdestructors
|
||||
|
||||
when not defined(leanCompiler):
|
||||
@@ -50,8 +50,8 @@ proc addForwardedProc(m: BModule, prc: PSym) =
|
||||
m.g.forwardedProcs.add(prc)
|
||||
|
||||
proc findPendingModule(m: BModule, s: PSym): BModule =
|
||||
var ms = getModule(s)
|
||||
result = m.g.modules[ms.position]
|
||||
let ms = s.itemId.module #getModule(s)
|
||||
result = m.g.modules[ms]
|
||||
|
||||
proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
|
||||
result.k = k
|
||||
@@ -97,10 +97,13 @@ proc getCFile(m: BModule): AbsoluteFile
|
||||
proc getModuleDllPath(m: BModule): Rope =
|
||||
let (dir, name, ext) = splitFile(getCFile(m))
|
||||
let filename = strutils.`%`(platform.OS[m.g.config.target.targetOS].dllFrmt, [name & ext])
|
||||
return makeCString(dir.string & "/" & filename)
|
||||
result = makeCString(dir.string & "/" & filename)
|
||||
|
||||
proc getModuleDllPath(m: BModule, module: int): Rope =
|
||||
result = getModuleDllPath(m.g.modules[module])
|
||||
|
||||
proc getModuleDllPath(m: BModule, s: PSym): Rope =
|
||||
return getModuleDllPath(findPendingModule(m, s))
|
||||
result = getModuleDllPath(m.g.modules[s.itemId.module])
|
||||
|
||||
import macros
|
||||
|
||||
@@ -406,7 +409,12 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
if isImportedCppType(typ): return
|
||||
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
|
||||
assert rdLoc(loc) != nil
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
|
||||
let atyp = skipTypes(loc.t, abstractInst)
|
||||
if atyp.kind in {tyVar, tyLent}:
|
||||
linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
elif not isComplexValueType(typ):
|
||||
if containsGcRef:
|
||||
var nilLoc: TLoc
|
||||
@@ -855,7 +863,8 @@ proc containsResult(n: PNode): bool =
|
||||
for i in 0..<n.safeLen:
|
||||
if containsResult(n[i]): return true
|
||||
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, nkMacroDef} +
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
|
||||
nkMacroDef, nkMixinStmt, nkBindStmt} +
|
||||
declarativeDefs
|
||||
|
||||
proc easyResultAsgn(n: PNode): PNode =
|
||||
@@ -1103,7 +1112,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
|
||||
useHeader(m, sym)
|
||||
if lfNoDecl in sym.loc.flags: return
|
||||
if lfDynamicLib in sym.loc.flags:
|
||||
if getModule(sym).id != m.module.id and
|
||||
if sym.itemId.module != m.module.position and
|
||||
not containsOrIncl(m.declaredThings, sym.id):
|
||||
m.s[cfsVars].add(ropecg(m, "$1 $2 $3;$n",
|
||||
[(if isReloadable(m, sym): "static" else: "extern"),
|
||||
@@ -1144,7 +1153,9 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
#if prc.loc.k == locNone:
|
||||
# mangle the inline proc based on the module where it is defined -
|
||||
# not on the first module that uses it
|
||||
fillProcLoc(findPendingModule(m, prc), prc.ast[namePos])
|
||||
let m2 = if m.config.symbolFiles != disabledSf: m
|
||||
else: findPendingModule(m, prc)
|
||||
fillProcLoc(m2, prc.ast[namePos])
|
||||
#elif {sfExportc, sfImportc} * prc.flags == {}:
|
||||
# # reset name to restore consistency in case of hashing collisions:
|
||||
# echo "resetting ", prc.id, " by ", m.module.name.s
|
||||
@@ -1189,48 +1200,16 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
|
||||
|
||||
proc requestConstImpl(p: BProc, sym: PSym) =
|
||||
var m = p.module
|
||||
useHeader(m, sym)
|
||||
if sym.loc.k == locNone:
|
||||
fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
|
||||
if m.hcrOn: incl(sym.loc.flags, lfIndirect)
|
||||
|
||||
if lfNoDecl in sym.loc.flags: return
|
||||
# declare implementation:
|
||||
var q = findPendingModule(m, sym)
|
||||
if q != nil and not containsOrIncl(q.declaredThings, sym.id):
|
||||
assert q.initProc.module == q
|
||||
# add a suffix for hcr - will later init the global pointer with this data
|
||||
let actualConstName = if m.hcrOn: sym.loc.r & "_const" else: sym.loc.r
|
||||
q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n",
|
||||
[getTypeDesc(q, sym.typ), actualConstName,
|
||||
genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)])
|
||||
if m.hcrOn:
|
||||
# generate the global pointer with the real name
|
||||
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r])
|
||||
# register it (but ignore the boolean result of hcrRegisterGlobal)
|
||||
q.initProc.procSec(cpsLocals).addf(
|
||||
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
|
||||
[getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
|
||||
# always copy over the contents of the actual constant with the _const
|
||||
# suffix ==> this means that the constant is reloadable & updatable!
|
||||
q.initProc.procSec(cpsLocals).add(ropecg(q,
|
||||
"\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
|
||||
[sym.loc.r, actualConstName, rdLoc(sym.loc)]))
|
||||
# declare header:
|
||||
if q != m and not containsOrIncl(m.declaredThings, sym.id):
|
||||
assert(sym.loc.r != nil)
|
||||
if m.hcrOn:
|
||||
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
|
||||
m.initProc.procSec(cpsLocals).addf(
|
||||
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
||||
getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
|
||||
else:
|
||||
let headerDecl = "extern NIM_CONST $1 $2;$n" %
|
||||
[getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
|
||||
m.s[cfsData].add(headerDecl)
|
||||
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
|
||||
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
|
||||
if genConstSetup(p, sym):
|
||||
let m = p.module
|
||||
# declare implementation:
|
||||
var q = findPendingModule(m, sym)
|
||||
if q != nil and not containsOrIncl(q.declaredThings, sym.id):
|
||||
assert q.initProc.module == q
|
||||
genConstDefinition(q, p, sym)
|
||||
# declare header:
|
||||
if q != m and not containsOrIncl(m.declaredThings, sym.id):
|
||||
genConstHeader(m, q, p, sym)
|
||||
|
||||
proc isActivated(prc: PSym): bool = prc.typ != nil
|
||||
|
||||
@@ -1610,9 +1589,7 @@ proc genDatInitCode(m: BModule) =
|
||||
for i in cfsTypeInit1..cfsDynLibInit:
|
||||
if m.s[i].len != 0:
|
||||
moduleDatInitRequired = true
|
||||
prc.add(genSectionStart(i, m.config))
|
||||
prc.add(m.s[i])
|
||||
prc.add(genSectionEnd(i, m.config))
|
||||
|
||||
prc.addf("}$N$N", [])
|
||||
|
||||
@@ -1670,9 +1647,7 @@ proc genInitCode(m: BModule) =
|
||||
if m.thing.s(section).len > 0:
|
||||
moduleInitRequired = true
|
||||
if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n")
|
||||
prc.add(genSectionStart(section, m.config))
|
||||
prc.add(m.thing.s(section))
|
||||
prc.add(genSectionEnd(section, m.config))
|
||||
if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n")
|
||||
|
||||
if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
|
||||
@@ -1684,7 +1659,7 @@ proc genInitCode(m: BModule) =
|
||||
writeSection(preInitProc, cpsLocals)
|
||||
writeSection(preInitProc, cpsInit, m.hcrOn)
|
||||
writeSection(preInitProc, cpsStmts)
|
||||
prc.addf("}$N", [])
|
||||
prc.addf("}/* preInitProc end */$N", [])
|
||||
when false:
|
||||
m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals)
|
||||
m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit)
|
||||
@@ -1764,28 +1739,21 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||
var moduleIsEmpty = true
|
||||
|
||||
result = getFileHeader(m.config, cfile)
|
||||
result.add(genMergeInfo(m))
|
||||
|
||||
generateThreadLocalStorage(m)
|
||||
generateHeaders(m)
|
||||
result.add(genSectionStart(cfsHeaders, m.config))
|
||||
result.add(m.s[cfsHeaders])
|
||||
if m.config.cppCustomNamespace.len > 0:
|
||||
result.add openNamespaceNim(m.config.cppCustomNamespace)
|
||||
result.add(genSectionEnd(cfsHeaders, m.config))
|
||||
result.add(genSectionStart(cfsFrameDefines, m.config))
|
||||
if m.s[cfsFrameDefines].len > 0:
|
||||
result.add(m.s[cfsFrameDefines])
|
||||
else:
|
||||
result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n")
|
||||
result.add(genSectionEnd(cfsFrameDefines, m.config))
|
||||
|
||||
for i in cfsForwardTypes..cfsProcs:
|
||||
if m.s[i].len > 0:
|
||||
moduleIsEmpty = false
|
||||
result.add(genSectionStart(i, m.config))
|
||||
result.add(m.s[i])
|
||||
result.add(genSectionEnd(i, m.config))
|
||||
|
||||
if m.s[cfsInitProc].len > 0:
|
||||
moduleIsEmpty = false
|
||||
@@ -1839,7 +1807,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
|
||||
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
||||
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
|
||||
|
||||
proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
||||
proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
||||
# we should create only one cgen module for each module sym
|
||||
result = rawNewModule(g, module, conf)
|
||||
if module.position >= g.modules.len:
|
||||
@@ -1875,9 +1843,7 @@ proc writeHeader(m: BModule) =
|
||||
|
||||
generateThreadLocalStorage(m)
|
||||
for i in cfsHeaders..cfsProcs:
|
||||
result.add(genSectionStart(i, m.config))
|
||||
result.add(m.s[i])
|
||||
result.add(genSectionEnd(i, m.config))
|
||||
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: result.add openNamespaceNim(m.config.cppCustomNamespace)
|
||||
result.add(m.s[cfsInitProc])
|
||||
|
||||
@@ -1922,13 +1888,9 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
|
||||
|
||||
genStmts(p, n)
|
||||
|
||||
proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
if b == nil: return
|
||||
var m = BModule(b)
|
||||
if passes.skipCodegen(m.config, n) or
|
||||
not moduleHasChanged(m.g.graph, m.module):
|
||||
return
|
||||
proc genTopLevelStmt*(m: BModule; n: PNode) =
|
||||
## Also called from `ic/cbackend.nim`.
|
||||
if passes.skipCodegen(m.config, n): return
|
||||
m.initProc.options = initProcOptions(m)
|
||||
#softRnl = if optLineDir in m.config.options: noRnl else: rnl
|
||||
# XXX replicate this logic!
|
||||
@@ -1941,6 +1903,12 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||
else:
|
||||
genProcBody(m.initProc, transformedN)
|
||||
|
||||
proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
if b != nil:
|
||||
var m = BModule(b)
|
||||
genTopLevelStmt(m, n)
|
||||
|
||||
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
if optForceFullMake notin m.config.globalOptions:
|
||||
if not moduleHasChanged(m.g.graph, m.module):
|
||||
@@ -1974,46 +1942,26 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
proc writeModule(m: BModule, pending: bool) =
|
||||
template onExit() = close(m.ndi, m.config)
|
||||
let cfile = getCFile(m)
|
||||
if true or optForceFullMake in m.config.globalOptions:
|
||||
if moduleHasChanged(m.g.graph, m.module):
|
||||
genInitCode(m)
|
||||
finishTypeDescriptions(m)
|
||||
if sfMainModule in m.module.flags:
|
||||
# generate main file:
|
||||
genMainProc(m)
|
||||
m.s[cfsProcHeaders].add(m.g.mainModProcs)
|
||||
generateThreadVarsSize(m)
|
||||
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
var code = genModule(m, cf)
|
||||
if code != nil or m.config.symbolFiles != disabledSf:
|
||||
when hasTinyCBackend:
|
||||
if m.config.cmd == cmdTcc:
|
||||
tccgen.compileCCode($code, m.config)
|
||||
onExit()
|
||||
return
|
||||
|
||||
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
|
||||
let cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
mergeFiles(cfile, m)
|
||||
if moduleHasChanged(m.g.graph, m.module):
|
||||
genInitCode(m)
|
||||
finishTypeDescriptions(m)
|
||||
var code = genModule(m, cf)
|
||||
if code != nil:
|
||||
if not writeRope(code, cfile):
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||
addFileToCompile(m.config, cf)
|
||||
else:
|
||||
# Consider: first compilation compiles ``system.nim`` and produces
|
||||
# ``system.c`` but then compilation fails due to an error. This means
|
||||
# that ``system.o`` is missing, so we need to call the C compiler for it:
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
if not fileExists(cf.obj): cf.flags = {CfileFlag.Cached}
|
||||
if sfMainModule in m.module.flags:
|
||||
# generate main file:
|
||||
genMainProc(m)
|
||||
m.s[cfsProcHeaders].add(m.g.mainModProcs)
|
||||
generateThreadVarsSize(m)
|
||||
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
var code = genModule(m, cf)
|
||||
if code != nil or m.config.symbolFiles != disabledSf:
|
||||
when hasTinyCBackend:
|
||||
if m.config.cmd == cmdTcc:
|
||||
tccgen.compileCCode($code, m.config)
|
||||
onExit()
|
||||
return
|
||||
|
||||
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
onExit()
|
||||
|
||||
@@ -2021,26 +1969,13 @@ proc updateCachedModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
if sfMainModule notin m.module.flags:
|
||||
genMainProc(m)
|
||||
cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
if mergeRequired(m) and sfMainModule notin m.module.flags:
|
||||
mergeFiles(cfile, m)
|
||||
genInitCode(m)
|
||||
finishTypeDescriptions(m)
|
||||
var code = genModule(m, cf)
|
||||
if code != nil:
|
||||
if not writeRope(code, cfile):
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.string)
|
||||
addFileToCompile(m.config, cf)
|
||||
else:
|
||||
if sfMainModule notin m.module.flags:
|
||||
genMainProc(m)
|
||||
cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
if b == nil: return
|
||||
var m = BModule(b)
|
||||
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
|
||||
## Also called from IC.
|
||||
if sfMainModule in m.module.flags:
|
||||
# phase ordering problem here: We need to announce this
|
||||
# dependency to 'nimTestErrorFlag' before system.c has been written to disk.
|
||||
@@ -2091,6 +2026,12 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
let mm = m
|
||||
m.g.modulesClosed.add mm
|
||||
|
||||
|
||||
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
if b == nil: return
|
||||
finalCodegenActions(graph, BModule(b), n)
|
||||
|
||||
proc genForwardedProcs(g: BModuleList) =
|
||||
# Forward declared proc:s lack bodies when first encountered, so they're given
|
||||
# a second pass here
|
||||
@@ -2098,8 +2039,7 @@ proc genForwardedProcs(g: BModuleList) =
|
||||
while g.forwardedProcs.len > 0:
|
||||
let
|
||||
prc = g.forwardedProcs.pop()
|
||||
ms = getModule(prc)
|
||||
m = g.modules[ms.position]
|
||||
m = g.modules[prc.itemId.module]
|
||||
if sfForward in prc.flags:
|
||||
internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ type
|
||||
|
||||
TTypeSeq* = seq[PType]
|
||||
TypeCache* = Table[SigHash, Rope]
|
||||
TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: PSym]]
|
||||
TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]]
|
||||
|
||||
CodegenFlag* = enum
|
||||
preventStackTrace, # true if stack traces need to be prevented
|
||||
@@ -112,7 +112,8 @@ type
|
||||
isHeaderFile, # C source file is the header file
|
||||
includesStringh, # C source file already includes ``<string.h>``
|
||||
objHasKidsValid # whether we can rely on tfObjHasKids
|
||||
|
||||
useAliveDataFromDce # use the `alive: IntSet` field instead of
|
||||
# computing alive data on our own.
|
||||
|
||||
BModuleList* = ref object of RootObj
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
@@ -154,6 +155,7 @@ type
|
||||
forwTypeCache*: TypeCache # cache for forward declarations of types
|
||||
declaredThings*: IntSet # things we have declared in this .c file
|
||||
declaredProtos*: IntSet # prototypes we have declared in this .c file
|
||||
alive*: IntSet # symbol IDs of alive data as computed by `dce.nim`
|
||||
headerFiles*: seq[string] # needed headers to include
|
||||
typeInfoMarker*: TypeCache # needed for generating type information
|
||||
typeInfoMarkerV2*: TypeCache
|
||||
@@ -200,7 +202,7 @@ proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result.sigConflicts = initCountTable[string]()
|
||||
|
||||
proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: PSym]](),
|
||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
|
||||
config: g.config, graph: g, nimtvDeclared: initIntSet())
|
||||
|
||||
iterator cgenModules*(g: BModuleList): BModule =
|
||||
|
||||
@@ -67,7 +67,7 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
while true:
|
||||
aa = skipTypes(aa, {tyGenericInst, tyAlias})
|
||||
bb = skipTypes(bb, {tyGenericInst, tyAlias})
|
||||
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}:
|
||||
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
|
||||
aa = aa.lastSon
|
||||
bb = bb.lastSon
|
||||
else:
|
||||
@@ -107,11 +107,14 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||
s.ast[resultPos] = newNodeI(nkEmpty, s.info)
|
||||
s.ast[dispatcherPos] = dispatcher
|
||||
|
||||
proc createDispatcher(s: PSym; idgen: IdGenerator): PSym =
|
||||
var disp = copySym(s, nextId(idgen))
|
||||
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
var disp = copySym(s, nextSymId(idgen))
|
||||
incl(disp.flags, sfDispatcher)
|
||||
excl(disp.flags, sfExported)
|
||||
disp.typ = copyType(disp.typ, nextId(idgen), disp.typ.owner)
|
||||
let old = disp.typ
|
||||
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
|
||||
copyTypeProps(g, idgen.module, disp.typ, old)
|
||||
|
||||
# we can't inline the dispatcher itself (for now):
|
||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
||||
disp.ast = copyTree(s.ast)
|
||||
@@ -119,7 +122,7 @@ proc createDispatcher(s: PSym; idgen: IdGenerator): PSym =
|
||||
disp.loc.r = nil
|
||||
if s.typ[0] != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextId(idgen))
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
||||
else:
|
||||
# We've encountered a method prototype without a filled-in
|
||||
# resultPos slot. We put a placeholder in there that will
|
||||
@@ -157,7 +160,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||
if disp.typ.lockLevel < meth.typ.lockLevel:
|
||||
disp.typ.lockLevel = meth.typ.lockLevel
|
||||
|
||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) =
|
||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
var witness: PSym
|
||||
for i in 0..<g.methods.len:
|
||||
let disp = g.methods[i].dispatcher
|
||||
@@ -177,10 +180,8 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) =
|
||||
of Invalid:
|
||||
if witness.isNil: witness = g.methods[i].methods[0]
|
||||
# create a new dispatcher:
|
||||
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, idgen)))
|
||||
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
|
||||
#echo "adding ", s.info
|
||||
#if fromCache:
|
||||
# internalError(s.info, "no method dispatcher found")
|
||||
if witness != nil:
|
||||
localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
|
||||
"' to method defined here: " & g.config$witness.info)
|
||||
|
||||
@@ -157,7 +157,7 @@ type
|
||||
|
||||
const
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt} + procDefs
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
|
||||
|
||||
proc newStateAccess(ctx: var Ctx): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
@@ -177,7 +177,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
|
||||
ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
|
||||
|
||||
proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), nextId(ctx.idgen), ctx.fn, ctx.fn.info)
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info)
|
||||
result.typ = typ
|
||||
assert(not typ.isNil)
|
||||
|
||||
@@ -427,7 +427,8 @@ proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
|
||||
|
||||
proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
|
||||
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 =
|
||||
result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
|
||||
@@ -1118,9 +1119,9 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
|
||||
n[i] = ctx.skipThroughEmptyStates(n[i])
|
||||
|
||||
proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: PSym): PType =
|
||||
result = newType(tyArray, nextId(idgen), owner)
|
||||
result = newType(tyArray, nextTypeId(idgen), owner)
|
||||
|
||||
let rng = newType(tyRange, nextId(idgen), owner)
|
||||
let rng = newType(tyRange, nextTypeId(idgen), owner)
|
||||
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
|
||||
rng.rawAddSon(t)
|
||||
|
||||
@@ -1314,7 +1315,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
|
||||
let idefs = copyNode(it)
|
||||
for v in 0..it.len-3:
|
||||
if it[v].kind == nkSym:
|
||||
let x = copySym(it[v].sym, nextId(c.idgen))
|
||||
let x = copySym(it[v].sym, nextSymId(c.idgen))
|
||||
c.tab[it[v].sym.id] = x
|
||||
idefs.add newSymNode(x)
|
||||
else:
|
||||
@@ -1393,9 +1394,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
# Lambda lifting was not done yet. Use temporary :state sym, which will
|
||||
# be handled specially by lambda lifting. Local temp vars (if needed)
|
||||
# should follow the same logic.
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextId(idgen), fn, fn.info)
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextId(idgen), fn, fn.info)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info)
|
||||
var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
|
||||
var n = preprocess(pc, n.toStmtList)
|
||||
#echo "transformed into ", n
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
|
||||
# We do this here before the 'import' statement so 'defined' does not get
|
||||
# confused with 'TGCMode.gcGenerational' etc.
|
||||
# confused with 'TGCMode.gcMarkAndSweep' etc.
|
||||
template bootSwitch(name, expr, userString) =
|
||||
# Helper to build boot constants, for debugging you can 'echo' the else part.
|
||||
const name = if expr: " " & userString else: ""
|
||||
@@ -22,7 +22,6 @@ bootSwitch(usedDanger, defined(danger), "-d:danger")
|
||||
bootSwitch(useLinenoise, defined(nimUseLinenoise) or defined(useLinenoise), "-d:nimUseLinenoise")
|
||||
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
|
||||
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
|
||||
bootSwitch(usedGenerational, defined(gcgenerational), "--gc:generational")
|
||||
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
|
||||
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
|
||||
|
||||
@@ -31,7 +30,6 @@ import
|
||||
wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
|
||||
pathutils, strtabs
|
||||
|
||||
from incremental import nimIncremental
|
||||
from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect
|
||||
|
||||
# but some have deps to imported modules. Yay.
|
||||
@@ -98,12 +96,13 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
|
||||
{msgStdout})
|
||||
|
||||
const gitHash {.strdefine.} = gorge("git log -n 1 --format=%H").strip
|
||||
# xxx move this logic to std/private/gitutils
|
||||
when gitHash.len == 40:
|
||||
msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
|
||||
|
||||
msgWriteln(conf, "active boot switches:" & usedRelease & usedDanger &
|
||||
usedTinyC & useLinenoise & usedNativeStacktrace &
|
||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
|
||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGoGC & usedNoGC,
|
||||
{msgStdout})
|
||||
msgQuit(0)
|
||||
|
||||
@@ -192,7 +191,8 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
if i == arg.len: discard
|
||||
elif i < arg.len and (arg[i] in {':', '='}): inc(i)
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
if state == wHint:
|
||||
# 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)
|
||||
@@ -210,13 +210,13 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
incl(conf.modifiedyNotes, n)
|
||||
case val
|
||||
of "on":
|
||||
if state == wWarningAsError:
|
||||
incl(conf.warningAsErrors, n)
|
||||
if state in {wWarningAsError, wHintAsError}:
|
||||
incl(conf.warningAsErrors, n) # xxx rename warningAsErrors to noteAsErrors
|
||||
else:
|
||||
incl(conf.notes, n)
|
||||
incl(conf.mainPackageNotes, n)
|
||||
of "off":
|
||||
if state == wWarningAsError:
|
||||
if state in {wWarningAsError, wHintAsError}:
|
||||
excl(conf.warningAsErrors, n)
|
||||
else:
|
||||
excl(conf.notes, n)
|
||||
@@ -234,21 +234,26 @@ const
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
|
||||
template warningOptionNoop(switch: string) =
|
||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||
|
||||
template deprecatedAlias(oldName, newName: string) =
|
||||
warningDeprecated(conf, info, "'$#' is a deprecated alias for '$#'" % [oldName, newName])
|
||||
|
||||
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
||||
case switch.normalize
|
||||
of "gc":
|
||||
case arg.normalize
|
||||
of "boehm": result = conf.selectedGC == gcBoehm
|
||||
of "refc": result = conf.selectedGC == gcRefc
|
||||
of "v2": result = false
|
||||
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
|
||||
of "generational": result = false
|
||||
of "destructors", "arc": result = conf.selectedGC == gcArc
|
||||
of "orc": result = conf.selectedGC == gcOrc
|
||||
of "hooks": result = conf.selectedGC == gcHooks
|
||||
of "go": result = conf.selectedGC == gcGo
|
||||
of "none": result = conf.selectedGC == gcNone
|
||||
of "stack", "regions": result = conf.selectedGC == gcRegions
|
||||
of "v2", "generational": warningOptionNoop(arg)
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "opt":
|
||||
case arg.normalize
|
||||
@@ -298,12 +303,13 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
result = conf.options * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
|
||||
of "infchecks": result = contains(conf.options, optInfCheck)
|
||||
of "nanchecks": result = contains(conf.options, optNaNCheck)
|
||||
of "nilchecks": result = false # not a thing
|
||||
of "objchecks": result = contains(conf.options, optObjCheck)
|
||||
of "fieldchecks": result = contains(conf.options, optFieldCheck)
|
||||
of "rangechecks": result = contains(conf.options, optRangeCheck)
|
||||
of "boundchecks": result = contains(conf.options, optBoundsCheck)
|
||||
of "refchecks": result = contains(conf.options, optRefCheck)
|
||||
of "refchecks":
|
||||
warningDeprecated(conf, info, "refchecks is deprecated!")
|
||||
result = contains(conf.options, optRefCheck)
|
||||
of "overflowchecks": result = contains(conf.options, optOverflowCheck)
|
||||
of "staticboundchecks": result = contains(conf.options, optStaticBoundsCheck)
|
||||
of "stylechecks": result = contains(conf.options, optStyleCheck)
|
||||
@@ -313,12 +319,13 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
of "symbolfiles": result = conf.symbolFiles != disabledSf
|
||||
of "genscript": result = contains(conf.globalOptions, optGenScript)
|
||||
of "threads": result = contains(conf.globalOptions, optThreads)
|
||||
of "taintmode": result = contains(conf.globalOptions, optTaintMode)
|
||||
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
||||
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
|
||||
of "patterns", "trmacros": result = contains(conf.options, optTrMacros)
|
||||
of "patterns", "trmacros":
|
||||
if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
|
||||
result = contains(conf.options, optTrMacros)
|
||||
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||
of "nilseqs": result = contains(conf.options, optNilSeqs)
|
||||
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
|
||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
||||
@@ -419,7 +426,7 @@ proc parseCommand*(command: string): Command =
|
||||
of "gendepend": cmdGendepend
|
||||
of "dump": cmdDump
|
||||
of "parse": cmdParse
|
||||
of "scan": cmdScan
|
||||
of "rod": cmdRod
|
||||
of "secret": cmdInteractive
|
||||
of "nop", "help": cmdNop
|
||||
of "jsonscript": cmdJsonscript
|
||||
@@ -439,6 +446,13 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||
conf.command = command
|
||||
setCmd(conf, command.parseCommand)
|
||||
# command early customizations
|
||||
# must be handled here to honor subsequent `--hint:x:on|off`
|
||||
case conf.cmd
|
||||
of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc.
|
||||
conf.foreignPackageNotes = {hintSuccessX}
|
||||
else:
|
||||
conf.foreignPackageNotes = foreignPackageNotesDefault
|
||||
|
||||
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf: ConfigRef) =
|
||||
@@ -459,7 +473,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
|
||||
else: processPath(conf, path, info), info)
|
||||
of "nimblepath", "babelpath":
|
||||
# keep the old name for compat
|
||||
if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath")
|
||||
if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions:
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var path = processPath(conf, arg, info, notRelativeToProj=true)
|
||||
@@ -468,6 +482,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
path = nimbleDir / RelativeDir"pkgs"
|
||||
nimblePath(conf, path, info)
|
||||
of "nonimblepath", "nobabelpath":
|
||||
if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath")
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
disableNimblePath(conf)
|
||||
of "clearnimblepath":
|
||||
@@ -501,8 +516,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
|
||||
conf.backend = backend
|
||||
of "doccmd": conf.docCmd = arg
|
||||
of "mainmodule", "m":
|
||||
discard "allow for backwards compatibility, but don't do anything"
|
||||
of "define", "d":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if {':', '='} in arg:
|
||||
@@ -517,9 +530,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "undef", "u":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
undefSymbol(conf.symbols, arg)
|
||||
of "symbol":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# deprecated, do nothing
|
||||
of "compile":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}: processCompile(conf, arg)
|
||||
@@ -552,8 +562,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
|
||||
of "refc":
|
||||
conf.selectedGC = gcRefc
|
||||
of "v2":
|
||||
message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
|
||||
of "markandsweep":
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
@@ -566,6 +574,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
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")
|
||||
@@ -575,6 +585,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
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")
|
||||
@@ -591,15 +603,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "stack", "regions":
|
||||
conf.selectedGC = gcRegions
|
||||
defineSymbol(conf.symbols, "gcregions")
|
||||
of "v2": warningOptionNoop(arg)
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "warnings", "w":
|
||||
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
||||
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
||||
of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf)
|
||||
of "warningaserror": processSpecificNote(arg, wWarningAsError, pass, info, switch, conf)
|
||||
of "hintaserror": processSpecificNote(arg, wHintAsError, pass, info, switch, conf)
|
||||
of "hints":
|
||||
if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf)
|
||||
of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
|
||||
of "threadanalysis":
|
||||
if conf.backend == backendJs: discard
|
||||
else: processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info)
|
||||
of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info)
|
||||
of "stacktracemsgs": processOnOffSwitch(conf, {optStackTraceMsgs}, arg, pass, info)
|
||||
of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info)
|
||||
@@ -638,32 +654,32 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
else:
|
||||
undefSymbol(conf.symbols, "hotcodereloading")
|
||||
undefSymbol(conf.symbols, "useNimRtl")
|
||||
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
|
||||
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
|
||||
of "floatchecks":
|
||||
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
|
||||
of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info)
|
||||
of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info)
|
||||
of "nilchecks": discard "no such thing anymore"
|
||||
of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info)
|
||||
of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
|
||||
of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
|
||||
of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
|
||||
of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
|
||||
of "refchecks":
|
||||
warningDeprecated(conf, info, "refchecks is deprecated!")
|
||||
processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
|
||||
of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
|
||||
of "staticboundchecks": processOnOffSwitch(conf, {optStaticBoundsCheck}, arg, pass, info)
|
||||
of "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info)
|
||||
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
|
||||
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
|
||||
of "deadcodeelim": discard # deprecated, dead code elim always on
|
||||
of "threads":
|
||||
processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
||||
if conf.backend == backendJs: discard
|
||||
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
||||
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
|
||||
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
|
||||
of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info)
|
||||
of "implicitstatic":
|
||||
processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
|
||||
of "patterns", "trmacros":
|
||||
if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
|
||||
processOnOffSwitch(conf, {optTrMacros}, arg, pass, info)
|
||||
of "opt":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
@@ -790,19 +806,18 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "help", "h":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
helpOnError(conf, pass)
|
||||
of "symbolfiles": discard "ignore for backwards compat"
|
||||
of "incremental":
|
||||
when not nimIncremental:
|
||||
localError(conf, info, "the compiler was not built with " &
|
||||
"incremental compilation features; bootstrap with " &
|
||||
"-d:nimIncremental to enable")
|
||||
case arg.normalize
|
||||
of "on": conf.symbolFiles = v2Sf
|
||||
of "off": conf.symbolFiles = disabledSf
|
||||
of "writeonly": conf.symbolFiles = writeOnlySf
|
||||
of "readonly": conf.symbolFiles = readOnlySf
|
||||
of "v2": conf.symbolFiles = v2Sf
|
||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||
of "symbolfiles", "incremental", "ic":
|
||||
if switch.normalize == "symbolfiles": deprecatedAlias(switch, "incremental")
|
||||
# xxx maybe also ic, since not in help?
|
||||
if pass in {passCmd2, passPP}:
|
||||
case arg.normalize
|
||||
of "on": conf.symbolFiles = v2Sf
|
||||
of "off": conf.symbolFiles = disabledSf
|
||||
of "writeonly": conf.symbolFiles = writeOnlySf
|
||||
of "readonly": conf.symbolFiles = readOnlySf
|
||||
of "v2": conf.symbolFiles = v2Sf
|
||||
of "stress": conf.symbolFiles = stressTest
|
||||
else: localError(conf, info, "invalid option for --incremental: " & arg)
|
||||
of "skipcfg":
|
||||
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
|
||||
of "skipprojcfg":
|
||||
@@ -812,6 +827,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "skipparentcfg":
|
||||
processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
|
||||
of "genscript", "gendeps":
|
||||
if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
|
||||
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
||||
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
||||
@@ -847,15 +863,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
|
||||
of "listfullpaths":
|
||||
processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
|
||||
of "spellsuggest":
|
||||
if arg.len == 0: conf.spellSuggestMax = spellSuggestSecretSauce
|
||||
elif arg == "auto": conf.spellSuggestMax = spellSuggestSecretSauce
|
||||
else: conf.spellSuggestMax = parseInt(arg)
|
||||
of "declaredlocs":
|
||||
processOnOffSwitchG(conf, {optDeclaredLocs}, arg, pass, info)
|
||||
of "dynliboverride":
|
||||
dynlibOverride(conf, switch, arg, pass, info)
|
||||
of "dynliboverrideall":
|
||||
processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
|
||||
of "cs":
|
||||
# only supported for compatibility. Does nothing.
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
of "experimental":
|
||||
if arg.len == 0:
|
||||
conf.features.incl oldExperimentalFeatures
|
||||
@@ -885,6 +902,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if conf != nil:
|
||||
conf.cppDefine(arg)
|
||||
of "newruntime":
|
||||
warningDeprecated(conf, info, "newruntime is deprecated, use arc/orc instead!")
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
doAssert(conf != nil)
|
||||
@@ -931,8 +949,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "1.0":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "0")
|
||||
# always be compatible with 1.0.100:
|
||||
defineSymbol(conf.symbols, "NimPatch", "100")
|
||||
# old behaviors go here:
|
||||
defineSymbol(conf.symbols, "nimOldRelativePathBehavior")
|
||||
undefSymbol(conf.symbols, "nimDoesntTrackDefects")
|
||||
@@ -941,11 +957,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "1.2":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "2")
|
||||
# always be compatible with 1.2.100:
|
||||
defineSymbol(conf.symbols, "NimPatch", "100")
|
||||
conf.globalOptions.incl optNimV12Emulation
|
||||
else:
|
||||
localError(conf, info, "unknown Nim version; currently supported values are: {1.0}")
|
||||
localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
|
||||
# always be compatible with 1.x.100:
|
||||
defineSymbol(conf.symbols, "NimPatch", "100")
|
||||
of "benchmarkvm":
|
||||
processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
|
||||
of "profilevm":
|
||||
@@ -959,13 +975,14 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
|
||||
if optPanics in conf.globalOptions:
|
||||
defineSymbol(conf.symbols, "nimPanics")
|
||||
of "sourcemap":
|
||||
of "sourcemap": # xxx document in --fullhelp
|
||||
conf.globalOptions.incl optSourcemap
|
||||
conf.options.incl optLineDir
|
||||
of "deepcopy":
|
||||
processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
|
||||
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
||||
handleStdinInput(conf)
|
||||
of "nilseqs", "nilchecks", "mainmodule", "m", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
|
||||
else:
|
||||
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
|
||||
else: invalidCmdLineOption(conf, pass, switch, info)
|
||||
@@ -975,7 +992,6 @@ proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
|
||||
splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
|
||||
processSwitch(cmd, arg, pass, gCmdLineInfo, config)
|
||||
|
||||
|
||||
proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) =
|
||||
# hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
|
||||
# we transform it to (key = hint, val = [X]:off)
|
||||
|
||||
340
compiler/concepts.nim
Normal file
340
compiler/concepts.nim
Normal file
@@ -0,0 +1,340 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer,
|
||||
types, intsets
|
||||
|
||||
from magicsys import addSonSkipIntLit
|
||||
|
||||
const
|
||||
logBindings = false
|
||||
|
||||
## Code dealing with Concept declarations
|
||||
## --------------------------------------
|
||||
|
||||
proc declareSelf(c: PContext; info: TLineInfo) =
|
||||
## adds the magical 'Self' symbols to the current scope.
|
||||
let ow = getCurrOwner(c)
|
||||
let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info)
|
||||
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
|
||||
s.typ.flags.incl {tfUnresolved, tfPacked}
|
||||
s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow)
|
||||
addDecl(c, s, info)
|
||||
|
||||
proc isSelf*(t: PType): bool {.inline.} =
|
||||
## is this the magical 'Self' type?
|
||||
t.kind == tyTypeDesc and tfPacked in t.flags
|
||||
|
||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
if typ.kind == tyTypeDesc and not isSelf(typ):
|
||||
result = typ
|
||||
else:
|
||||
result = newTypeS(tyTypeDesc, c)
|
||||
incl result.flags, tfCheckedForDestructor
|
||||
result.addSonSkipIntLit(typ, c.idgen)
|
||||
|
||||
proc semConceptDecl(c: PContext; n: PNode): PNode =
|
||||
## Recursive helper for semantic checking for the concept declaration.
|
||||
## Currently we only support lists of statements containing 'proc'
|
||||
## declarations and the like.
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = semConceptDecl(c, n[i])
|
||||
of nkProcDef..nkIteratorDef, nkFuncDef:
|
||||
result = c.semExpr(c, n, {efWantStmt})
|
||||
of nkTypeClassTy:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len-1:
|
||||
result[i] = n[i]
|
||||
result[^1] = semConceptDecl(c, n[^1])
|
||||
else:
|
||||
localError(c.config, n.info, "unexpected construct in the new-styled concept " & renderTree(n))
|
||||
result = n
|
||||
|
||||
proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
||||
## Semantic checking for the concept declaration. Runs
|
||||
## when we process the concept itself, not its matching process.
|
||||
assert n.kind == nkTypeClassTy
|
||||
inc c.inConceptDecl
|
||||
openScope(c)
|
||||
declareSelf(c, n.info)
|
||||
result = semConceptDecl(c, n)
|
||||
rawCloseScope(c)
|
||||
dec c.inConceptDecl
|
||||
|
||||
## Concept matching
|
||||
## ----------------
|
||||
|
||||
type
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
|
||||
## that turned out to be wrong.
|
||||
marker: IntSet ## Some protection against wild runaway recursions.
|
||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
## concrete type.
|
||||
for i in 0..<m.inferred.len:
|
||||
if m.inferred[i][0] == key: return m.inferred[i][1]
|
||||
return nil
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## the heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
## of a routine that might match.
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.lastSon, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
#let oldLen = m.inferred.len
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
|
||||
#m.inferred.setLen oldLen
|
||||
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
else:
|
||||
if a.kind == tyTypeDesc and f.len == a.len:
|
||||
for i in 0..<a.len:
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
return true
|
||||
|
||||
of tyGenericInvocation:
|
||||
if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
|
||||
if sameType(f[0], a[0]) and f.len == a.len-1:
|
||||
for i in 1 ..< f.len:
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
return true
|
||||
of tyGenericParam:
|
||||
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
|
||||
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
|
||||
result = false
|
||||
else:
|
||||
let old = existingBinding(m, f)
|
||||
if old == nil:
|
||||
if f.len > 0 and f[0].kind != tyNone:
|
||||
# also check the generic's constraints:
|
||||
let oldLen = m.inferred.len
|
||||
result = matchType(c, f[0], a, m)
|
||||
m.inferred.setLen oldLen
|
||||
if result:
|
||||
when logBindings: echo "A adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCString, tyString}:
|
||||
when logBindings: echo "B adding ", f, " ", lastSon ak
|
||||
m.inferred.add((f, lastSon ak))
|
||||
result = true
|
||||
else:
|
||||
when logBindings: echo "C adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
#echo "binding ", typeToString(ak), " to ", typeToString(f)
|
||||
result = true
|
||||
elif not m.marker.containsOrIncl(old.id):
|
||||
result = matchType(c, old, ak, m)
|
||||
if m.magic == mArrPut and ak.kind == tyGenericParam:
|
||||
result = true
|
||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
|
||||
of tyVar, tySink, tyLent, tyOwned:
|
||||
# modifiers in the concept must be there in the actual implementation
|
||||
# too but not vice versa.
|
||||
if a.kind == f.kind:
|
||||
result = matchType(c, f.sons[0], a.sons[0], m)
|
||||
elif m.magic == mArrPut:
|
||||
result = matchType(c, f.sons[0], a, m)
|
||||
else:
|
||||
result = false
|
||||
of tyEnum, tyObject, tyDistinct:
|
||||
result = sameType(f, a)
|
||||
of tyEmpty, tyString, tyCString, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
|
||||
result = a.skipTypes(ignorableForArgType).kind == f.kind
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
let ak = a.skipTypes(ignorableForArgType)
|
||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
||||
(ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal)
|
||||
of tyConcept:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
||||
tyGenericInst:
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.len == ak.len:
|
||||
for i in 0..<ak.len:
|
||||
if not matchType(c, f[i], ak[i], m): return false
|
||||
return true
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
# say the concept requires 'int|float|string' if the potentialImplementation
|
||||
# says 'int|string' that is good enough.
|
||||
var covered = 0
|
||||
for i in 0..<f.len:
|
||||
for j in 0..<a.len:
|
||||
let oldLenB = m.inferred.len
|
||||
let r = matchType(c, f[i], a[j], m)
|
||||
if r:
|
||||
inc covered
|
||||
break
|
||||
m.inferred.setLen oldLenB
|
||||
|
||||
result = covered >= a.len
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
else:
|
||||
for i in 0..<f.len:
|
||||
result = matchType(c, f[i], a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.inferred.setLen oldLen
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f[0], a[0], m)
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
result = not matchType(c, f[0], a, m)
|
||||
m.inferred.setLen oldLen
|
||||
of tyAnything:
|
||||
result = true
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## Like 'matchType' but with extra logic dealing with proc return types
|
||||
## which can be nil or the 'void' type.
|
||||
if f.isEmptyType:
|
||||
result = a.isEmptyType
|
||||
elif a == nil:
|
||||
result = false
|
||||
else:
|
||||
result = matchType(c, f, a, m)
|
||||
|
||||
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
|
||||
## or similar.
|
||||
|
||||
# watch out: only add bindings after a completely successful match.
|
||||
let oldLen = m.inferred.len
|
||||
|
||||
let can = candidate.typ.n
|
||||
let con = n[0].sym.typ.n
|
||||
|
||||
if can.len < con.len:
|
||||
# too few arguments, cannot be a match:
|
||||
return false
|
||||
|
||||
let common = min(can.len, con.len)
|
||||
for i in 1 ..< common:
|
||||
if not matchType(c, con[i].typ, can[i].typ, m):
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m):
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
# all other parameters have to be optional parameters:
|
||||
for i in common ..< can.len:
|
||||
assert can[i].kind == nkSym
|
||||
if can[i].sym.ast == nil:
|
||||
# has too many arguments one of which is not optional:
|
||||
m.inferred.setLen oldLen
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||
let candidates = searchInScopesFilterBy(c, n[namePos].sym.name, kinds)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
if matchSym(c, candidate, n, m): return true
|
||||
result = false
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
|
||||
## can be matched with the current scope.
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for i in 0..<n.len:
|
||||
if not conceptMatchNode(c, n[i], m):
|
||||
return false
|
||||
return true
|
||||
of nkProcDef, nkFuncDef:
|
||||
# procs match any of: proc, template, macro, func, method, converter.
|
||||
# The others are more specific.
|
||||
# XXX: Enforce .noSideEffect for 'nkFuncDef'? But then what are the use cases...
|
||||
const filter = {skProc, skTemplate, skMacro, skFunc, skMethod, skConverter}
|
||||
result = matchSyms(c, n, filter, m)
|
||||
of nkTemplateDef:
|
||||
result = matchSyms(c, n, {skTemplate}, m)
|
||||
of nkMacroDef:
|
||||
result = matchSyms(c, n, {skMacro}, m)
|
||||
of nkConverterDef:
|
||||
result = matchSyms(c, n, {skConverter}, m)
|
||||
of nkMethodDef:
|
||||
result = matchSyms(c, n, {skMethod}, m)
|
||||
of nkIteratorDef:
|
||||
result = matchSyms(c, n, {skIterator}, m)
|
||||
else:
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
|
||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
||||
## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
|
||||
## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
|
||||
## 'C[S, T]' parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
||||
if result:
|
||||
for (a, b) in m.inferred:
|
||||
if b.kind == tyGenericParam:
|
||||
var dest = b
|
||||
while true:
|
||||
dest = existingBinding(m, dest)
|
||||
if dest == nil or dest.kind != tyGenericParam: break
|
||||
if dest != nil:
|
||||
bindings.idTablePut(a, dest)
|
||||
when logBindings: echo "A bind ", a, " ", dest
|
||||
else:
|
||||
bindings.idTablePut(a, b)
|
||||
when logBindings: echo "B bind ", a, " ", b
|
||||
# we have a match, so bind 'arg' itself to 'concpt':
|
||||
bindings.idTablePut(concpt, arg)
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1:
|
||||
# bind even more generic parameters
|
||||
assert invocation.kind == tyGenericInvocation
|
||||
for i in 1 ..< invocation.len:
|
||||
bindings.idTablePut(invocation[i], arg[i])
|
||||
@@ -34,55 +34,58 @@ proc countDefinedSymbols*(symbols: StringTableRef): int =
|
||||
proc initDefines*(symbols: StringTableRef) =
|
||||
# for bootstrapping purposes and old code:
|
||||
template defineSymbol(s) = symbols.defineSymbol(s)
|
||||
defineSymbol("nimhygiene")
|
||||
defineSymbol("niminheritable")
|
||||
defineSymbol("nimmixin")
|
||||
defineSymbol("nimeffects")
|
||||
defineSymbol("nimbabel")
|
||||
defineSymbol("nimcomputedgoto")
|
||||
defineSymbol("nimunion")
|
||||
defineSymbol("nimnewshared")
|
||||
defineSymbol("nimNewTypedesc")
|
||||
defineSymbol("nimrequiresnimframe")
|
||||
defineSymbol("nimparsebiggestfloatmagic")
|
||||
defineSymbol("nimalias")
|
||||
defineSymbol("nimlocks")
|
||||
defineSymbol("nimnode")
|
||||
defineSymbol("nimvarargstyped")
|
||||
defineSymbol("nimtypedescfixed")
|
||||
defineSymbol("nimKnowsNimvm")
|
||||
defineSymbol("nimArrIdx")
|
||||
defineSymbol("nimHasalignOf")
|
||||
defineSymbol("nimDistros")
|
||||
defineSymbol("nimHasCppDefine")
|
||||
defineSymbol("nimGenericInOutFlags")
|
||||
when false: defineSymbol("nimHasOpt")
|
||||
defineSymbol("nimNoArrayToCstringConversion")
|
||||
defineSymbol("nimHasRunnableExamples")
|
||||
defineSymbol("nimNewDot")
|
||||
defineSymbol("nimHasNilChecks")
|
||||
defineSymbol("nimSymKind")
|
||||
defineSymbol("nimVmEqIdent")
|
||||
defineSymbol("nimNoNil")
|
||||
defineSymbol("nimNoZeroTerminator")
|
||||
defineSymbol("nimNotNil")
|
||||
defineSymbol("nimVmExportFixed")
|
||||
defineSymbol("nimHasSymOwnerInMacro")
|
||||
defineSymbol("nimNewRuntime")
|
||||
defineSymbol("nimIncrSeqV3")
|
||||
defineSymbol("nimAshr")
|
||||
defineSymbol("nimNoNilSeqs")
|
||||
defineSymbol("nimNoNilSeqs2")
|
||||
defineSymbol("nimHasUserErrors")
|
||||
defineSymbol("nimUncheckedArrayTyp")
|
||||
defineSymbol("nimHasTypeof")
|
||||
defineSymbol("nimErrorProcCanHaveBody")
|
||||
defineSymbol("nimHasInstantiationOfInMacro")
|
||||
defineSymbol("nimHasHotCodeReloading")
|
||||
defineSymbol("nimHasNilSeqs")
|
||||
defineSymbol("nimHasSignatureHashInMacro")
|
||||
defineSymbol("nimHasDefault")
|
||||
defineSymbol("nimMacrosSizealignof")
|
||||
defineSymbol("nimhygiene") # deadcode
|
||||
defineSymbol("niminheritable") # deadcode
|
||||
defineSymbol("nimmixin") # deadcode
|
||||
defineSymbol("nimeffects") # deadcode
|
||||
defineSymbol("nimbabel") # deadcode
|
||||
defineSymbol("nimcomputedgoto") # deadcode
|
||||
defineSymbol("nimunion") # deadcode
|
||||
defineSymbol("nimnewshared") # deadcode
|
||||
defineSymbol("nimNewTypedesc") # deadcode
|
||||
defineSymbol("nimrequiresnimframe") # deadcode
|
||||
defineSymbol("nimparsebiggestfloatmagic") # deadcode
|
||||
defineSymbol("nimalias") # deadcode
|
||||
defineSymbol("nimlocks") # deadcode
|
||||
defineSymbol("nimnode") # deadcode pending `nimnode` reference in opengl package
|
||||
# refs https://github.com/nim-lang/opengl/pull/79
|
||||
defineSymbol("nimvarargstyped") # deadcode
|
||||
defineSymbol("nimtypedescfixed") # deadcode
|
||||
defineSymbol("nimKnowsNimvm") # deadcode
|
||||
defineSymbol("nimArrIdx") # deadcode
|
||||
defineSymbol("nimHasalignOf") # deadcode
|
||||
defineSymbol("nimDistros") # deadcode
|
||||
defineSymbol("nimHasCppDefine") # deadcode
|
||||
defineSymbol("nimGenericInOutFlags") # deadcode
|
||||
when false: defineSymbol("nimHasOpt") # deadcode
|
||||
defineSymbol("nimNoArrayToCstringConversion") # deadcode
|
||||
defineSymbol("nimHasRunnableExamples") # deadcode
|
||||
defineSymbol("nimNewDot") # deadcode
|
||||
defineSymbol("nimHasNilChecks") # deadcode
|
||||
defineSymbol("nimSymKind") # deadcode
|
||||
defineSymbol("nimVmEqIdent") # deadcode
|
||||
defineSymbol("nimNoNil") # deadcode
|
||||
defineSymbol("nimNoZeroTerminator") # deadcode
|
||||
defineSymbol("nimNotNil") # deadcode
|
||||
defineSymbol("nimVmExportFixed") # deadcode
|
||||
defineSymbol("nimHasSymOwnerInMacro") # deadcode
|
||||
defineSymbol("nimNewRuntime") # deadcode
|
||||
defineSymbol("nimIncrSeqV3") # xxx: turn this into deadcode
|
||||
defineSymbol("nimAshr") # deadcode
|
||||
defineSymbol("nimNoNilSeqs") # deadcode
|
||||
defineSymbol("nimNoNilSeqs2") # deadcode
|
||||
defineSymbol("nimHasUserErrors") # deadcode
|
||||
defineSymbol("nimUncheckedArrayTyp") # deadcode
|
||||
defineSymbol("nimHasTypeof") # deadcode
|
||||
defineSymbol("nimErrorProcCanHaveBody") # deadcode
|
||||
defineSymbol("nimHasInstantiationOfInMacro") # deadcode
|
||||
defineSymbol("nimHasHotCodeReloading") # deadcode
|
||||
defineSymbol("nimHasNilSeqs") # deadcode
|
||||
defineSymbol("nimHasSignatureHashInMacro") # deadcode
|
||||
defineSymbol("nimHasDefault") # deadcode
|
||||
defineSymbol("nimMacrosSizealignof") # deadcode
|
||||
|
||||
# > 0.20.0
|
||||
defineSymbol("nimNoZeroExtendMagic")
|
||||
defineSymbol("nimMacrosGetNodeId")
|
||||
for f in Feature:
|
||||
@@ -123,3 +126,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasEffectTraitsModule")
|
||||
defineSymbol("nimHasCastPragmaBlocks")
|
||||
defineSymbol("nimHasDeclaredLocs")
|
||||
defineSymbol("nimHasJsBigIntBackend")
|
||||
defineSymbol("nimHasWarningAsError")
|
||||
defineSymbol("nimHasHintAsError")
|
||||
defineSymbol("nimHasSpellSuggest")
|
||||
|
||||
117
compiler/dfa.nim
117
compiler/dfa.nim
@@ -29,9 +29,8 @@
|
||||
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
|
||||
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
|
||||
|
||||
import ast, types, intsets, lineinfos, renderer, asciitables
|
||||
|
||||
from patterns import sameTrees
|
||||
import ast, types, intsets, lineinfos, renderer
|
||||
import std/private/asciitables
|
||||
|
||||
type
|
||||
InstrKind* = enum
|
||||
@@ -456,6 +455,10 @@ proc genCase(c: var Con; n: PNode) =
|
||||
let isExhaustive = skipTypes(n[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
||||
|
||||
# we generate endings as a set of chained gotos, this is a bit awkward but it
|
||||
# ensures when recursively traversing the CFG for various analysis, we don't
|
||||
# artificially extended the life of each branch (for the purposes of DFA)
|
||||
# beyond the minimum amount.
|
||||
var endings: seq[TPosition] = @[]
|
||||
c.gen(n[0])
|
||||
for i in 1..<n.len:
|
||||
@@ -463,13 +466,14 @@ proc genCase(c: var Con; n: PNode) =
|
||||
if it.len == 1 or (i == n.len-1 and isExhaustive):
|
||||
# treat the last branch as 'else' if this is an exhaustive case statement.
|
||||
c.gen(it.lastSon)
|
||||
if endings.len != 0:
|
||||
c.patch(endings[^1])
|
||||
else:
|
||||
forkT(it.lastSon):
|
||||
c.gen(it.lastSon)
|
||||
if endings.len != 0:
|
||||
c.patch(endings[^1])
|
||||
endings.add c.gotoI(it.lastSon)
|
||||
for i in countdown(endings.high, 0):
|
||||
let endPos = endings[i]
|
||||
c.patch(endPos)
|
||||
|
||||
proc genBlock(c: var Con; n: PNode) =
|
||||
withBlock(n[0].sym):
|
||||
@@ -573,49 +577,84 @@ proc skipConvDfa*(n: PNode): PNode =
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc aliases*(obj, field: PNode): bool =
|
||||
var n = field
|
||||
var obj = obj
|
||||
while true:
|
||||
case obj.kind
|
||||
of {nkObjDownConv, nkObjUpConv, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}:
|
||||
obj = obj[0]
|
||||
of PathKinds1:
|
||||
obj = obj[1]
|
||||
else: break
|
||||
while true:
|
||||
if sameTrees(obj, n): return true
|
||||
case n.kind
|
||||
of PathKinds0:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
else: break
|
||||
type AliasKind* = enum
|
||||
yes, no, maybe
|
||||
|
||||
type InstrTargetKind* = enum
|
||||
None, Full, Partial
|
||||
proc aliases*(obj, field: PNode): AliasKind =
|
||||
# obj -> field:
|
||||
# x -> x: true
|
||||
# x -> x.f: true
|
||||
# x.f -> x: false
|
||||
# x.f -> x.f: true
|
||||
# x.f -> x.v: false
|
||||
# x -> x[0]: true
|
||||
# x[0] -> x: false
|
||||
# x[0] -> x[0]: true
|
||||
# x[0] -> x[1]: false
|
||||
# x -> x[i]: true
|
||||
# x[i] -> x: false
|
||||
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
|
||||
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
|
||||
template collectImportantNodes(result, n) =
|
||||
var result: seq[PNode]
|
||||
var n = n
|
||||
while true:
|
||||
case n.kind
|
||||
of PathKinds0 - {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
result.add n
|
||||
n = n[0]
|
||||
of nkSym:
|
||||
result.add n; break
|
||||
else: return no
|
||||
|
||||
proc instrTargets*(insloc, loc: PNode): InstrTargetKind =
|
||||
if sameTrees(insloc, loc) or insloc.aliases(loc):
|
||||
Full # x -> x; x -> x.f
|
||||
elif loc.aliases(insloc):
|
||||
Partial # x.f -> x
|
||||
else: None
|
||||
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 - {nkBracketExpr, nkHiddenDeref, nkDerefExpr}:
|
||||
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
of nkBracketExpr:
|
||||
# in a[i] the 'i' must be known
|
||||
if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}:
|
||||
n = n[0]
|
||||
else:
|
||||
return false
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
|
||||
# pointer indirection.
|
||||
|
||||
@@ -25,6 +25,7 @@ from std/private/globs import nativeToUnixPath
|
||||
const
|
||||
exportSection = skField
|
||||
docCmdSkip = "skip"
|
||||
DocColOffset = "## ".len # assuming that a space was added after ##
|
||||
|
||||
type
|
||||
TSections = array[TSymKind, Rope]
|
||||
@@ -57,6 +58,11 @@ type
|
||||
|
||||
PDoc* = ref TDocumentor ## Alias to type less.
|
||||
|
||||
proc prettyString(a: object): string =
|
||||
# xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906
|
||||
for k, v in fieldPairs(a):
|
||||
result.add k & ": " & $v & "\n"
|
||||
|
||||
proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): RelativeFile =
|
||||
## returns a relative file that will be appended to outDir
|
||||
let file2 = $file
|
||||
@@ -132,10 +138,12 @@ template declareClosures =
|
||||
of meNewSectionExpected: k = errNewSectionExpected
|
||||
of meGeneralParseError: k = errGeneralParseError
|
||||
of meInvalidDirective: k = errInvalidDirectiveX
|
||||
of meFootnoteMismatch: k = errFootnoteMismatch
|
||||
of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
|
||||
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
|
||||
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
|
||||
of mwUnsupportedField: k = warnFieldXNotSupported
|
||||
of mwRstStyle: k = warnRstStyle
|
||||
{.gcsafe.}:
|
||||
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
|
||||
|
||||
@@ -203,7 +211,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||
var outp: AbsoluteFile
|
||||
if filename.len == 0:
|
||||
let nameOnly = splitFile(d.filename).name
|
||||
outp = getNimcacheDir(conf) / RelativeDir(nameOnly) /
|
||||
# "snippets" needed, refs bug #17183
|
||||
outp = getNimcacheDir(conf) / "snippets".RelativeDir / RelativeDir(nameOnly) /
|
||||
RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim")
|
||||
elif isAbsolute(filename):
|
||||
outp = AbsoluteFile(filename)
|
||||
@@ -221,10 +230,14 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||
# backward compatibility hacks; interpolation commands should explicitly use `$`
|
||||
if cmd.startsWith "nim ": result = "$nim " & cmd[4..^1]
|
||||
else: result = cmd
|
||||
# factor with D20210224T221756
|
||||
result = result.replace("$1", "$options") % [
|
||||
"nim", os.getAppFilename().quoteShell,
|
||||
"libpath", quoteShell(d.conf.libpath),
|
||||
"docCmd", d.conf.docCmd,
|
||||
"backend", $d.conf.backend,
|
||||
"options", outp.quoteShell,
|
||||
# xxx `quoteShell` seems buggy if user passes options = "-d:foo somefile.nim"
|
||||
]
|
||||
let cmd = cmd.interpSnippetCmd
|
||||
rawMessage(conf, hintExecuting, cmd)
|
||||
@@ -310,8 +323,12 @@ proc genComment(d: PDoc, n: PNode): string =
|
||||
when false:
|
||||
# RFC: to preseve newlines in comments, this would work:
|
||||
comment = comment.replace("\n", "\n\n")
|
||||
renderRstToOut(d[], parseRst(comment, toFullPath(d.conf, n.info), toLinenumber(n.info),
|
||||
toColumn(n.info), (var dummy: bool; dummy), d.options, d.conf), result)
|
||||
renderRstToOut(d[],
|
||||
parseRst(comment, toFullPath(d.conf, n.info),
|
||||
toLinenumber(n.info),
|
||||
toColumn(n.info) + DocColOffset,
|
||||
(var dummy: bool; dummy), d.options, d.conf),
|
||||
result)
|
||||
|
||||
proc genRecCommentAux(d: PDoc, n: PNode): Rope =
|
||||
if n == nil: return nil
|
||||
@@ -324,8 +341,7 @@ proc genRecCommentAux(d: PDoc, n: PNode): Rope =
|
||||
result = genRecCommentAux(d, n[i])
|
||||
if result != nil: return
|
||||
else:
|
||||
when defined(nimNoNilSeqs): n.comment = ""
|
||||
else: n.comment = nil
|
||||
n.comment = ""
|
||||
|
||||
proc genRecComment(d: PDoc, n: PNode): Rope =
|
||||
if n == nil: return nil
|
||||
@@ -445,18 +461,6 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
|
||||
|
||||
proc exampleOutputDir(d: PDoc): AbsoluteDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
|
||||
|
||||
proc writeExample(d: PDoc; ex: PNode, rdoccmd: string) =
|
||||
if d.conf.errorCounter > 0: return
|
||||
let outputDir = d.exampleOutputDir
|
||||
createDir(outputDir)
|
||||
inc d.exampleCounter
|
||||
let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
|
||||
"_examples" & $d.exampleCounter & ".nim"))
|
||||
#let nimcache = outp.changeFileExt"" & "_nimcache"
|
||||
renderModule(ex, d.filename, outp.string, conf = d.conf)
|
||||
if rdoccmd notin d.exampleGroups: d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len)
|
||||
d.exampleGroups[rdoccmd].code.add "import r\"$1\"\n" % outp.string
|
||||
|
||||
proc runAllExamples(d: PDoc) =
|
||||
# This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however
|
||||
# using `-d:js` (etc) cannot work properly, e.g. would fail with `importjs`
|
||||
@@ -469,8 +473,9 @@ proc runAllExamples(d: PDoc) =
|
||||
writeFile(outp, group.code)
|
||||
# most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
|
||||
# via command line
|
||||
# D20210224T221756:here
|
||||
let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [
|
||||
"nim", os.getAppFilename(),
|
||||
"nim", quoteShell(os.getAppFilename()),
|
||||
"backend", $d.conf.backend,
|
||||
"path", quoteShell(d.conf.projectPath),
|
||||
"libpath", quoteShell(d.conf.libpath),
|
||||
@@ -480,12 +485,14 @@ proc runAllExamples(d: PDoc) =
|
||||
"docCmd", group.docCmd,
|
||||
]
|
||||
if os.execShellCmd(cmd) != 0:
|
||||
quit "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, $group[], cmd]
|
||||
quit "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, group[].prettyString, cmd]
|
||||
else:
|
||||
# keep generated source file `outp` to allow inspection.
|
||||
rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
|
||||
# removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
|
||||
|
||||
proc quoted(a: string): string = result.addQuoted(a)
|
||||
|
||||
proc prepareExample(d: PDoc; n: PNode): tuple[rdoccmd: string, code: string] =
|
||||
## returns `rdoccmd` and source code for this runnableExamples
|
||||
var rdoccmd = ""
|
||||
@@ -496,19 +503,48 @@ proc prepareExample(d: PDoc; n: PNode): tuple[rdoccmd: string, code: string] =
|
||||
if n1.kind notin nkStrKinds: globalError(d.conf, n1.info, "string litteral expected")
|
||||
rdoccmd = n1.strVal
|
||||
|
||||
var docComment = newTree(nkCommentStmt)
|
||||
let useRenderModule = false
|
||||
let loc = d.conf.toFileLineCol(n.info)
|
||||
let code = extractRunnableExamplesSource(d.conf, n)
|
||||
|
||||
docComment.comment = "autogenerated by docgen\nloc: $1\nrdoccmd: $2" % [loc, rdoccmd]
|
||||
var runnableExamples = newTree(nkStmtList,
|
||||
docComment,
|
||||
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
|
||||
runnableExamples.info = n.info
|
||||
let ret = extractRunnableExamplesSource(d.conf, n)
|
||||
for a in n.lastSon: runnableExamples.add a
|
||||
# we could also use `ret` instead here, to keep sources verbatim
|
||||
writeExample(d, runnableExamples, rdoccmd)
|
||||
result = (rdoccmd, ret)
|
||||
if d.conf.errorCounter > 0:
|
||||
return (rdoccmd, code)
|
||||
|
||||
let comment = "autogenerated by docgen\nloc: $1\nrdoccmd: $2" % [loc, rdoccmd]
|
||||
let outputDir = d.exampleOutputDir
|
||||
createDir(outputDir)
|
||||
inc d.exampleCounter
|
||||
let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" & ("_examples$1.nim" % $d.exampleCounter)))
|
||||
|
||||
if useRenderModule:
|
||||
var docComment = newTree(nkCommentStmt)
|
||||
docComment.comment = comment
|
||||
var runnableExamples = newTree(nkStmtList,
|
||||
docComment,
|
||||
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
|
||||
runnableExamples.info = n.info
|
||||
for a in n.lastSon: runnableExamples.add a
|
||||
|
||||
# buggy, refs bug #17292
|
||||
# still worth fixing as it can affect other code relying on `renderModule`,
|
||||
# so we keep this code path here for now, which could still be useful in some
|
||||
# other situations.
|
||||
renderModule(runnableExamples, outp.string, conf = d.conf)
|
||||
|
||||
else:
|
||||
let code2 = """
|
||||
#[
|
||||
$1
|
||||
]#
|
||||
import $2
|
||||
$3
|
||||
""" % [comment, d.filename.quoted, code]
|
||||
writeFile(outp.string, code2)
|
||||
|
||||
if rdoccmd notin d.exampleGroups:
|
||||
d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len)
|
||||
d.exampleGroups[rdoccmd].code.add "import $1\n" % outp.string.quoted
|
||||
result = (rdoccmd, code)
|
||||
when false:
|
||||
proc extractImports(n: PNode; result: PNode) =
|
||||
if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
|
||||
@@ -560,7 +596,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: Runnab
|
||||
"\n\\textbf{$1}\n", [msg.rope])
|
||||
inc d.listingCounter
|
||||
let id = $d.listingCounter
|
||||
dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"])
|
||||
dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""])
|
||||
var dest2 = ""
|
||||
renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex)
|
||||
dest.add dest2
|
||||
@@ -671,8 +707,8 @@ proc getRstName(n: PNode): PRstNode =
|
||||
case n.kind
|
||||
of nkPostfix: result = getRstName(n[1])
|
||||
of nkPragmaExpr: result = getRstName(n[0])
|
||||
of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName)
|
||||
of nkIdent: result = newRstNode(rnLeaf, n.ident.s)
|
||||
of nkSym: result = newRstLeaf(n.sym.renderDefinitionName)
|
||||
of nkIdent: result = newRstLeaf(n.ident.s)
|
||||
of nkAccQuoted:
|
||||
result = getRstName(n[0])
|
||||
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
|
||||
@@ -1209,7 +1245,7 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope =
|
||||
renderTocEntries(d[], j, 1, tmp)
|
||||
var toc = tmp.rope
|
||||
for i in TSymKind:
|
||||
var shouldSort = i in {skProc, skFunc} and groupedToc
|
||||
var shouldSort = i in routineKinds and groupedToc
|
||||
genSection(d, i, shouldSort)
|
||||
toc.add(d.toc[i])
|
||||
if toc != nil:
|
||||
@@ -1225,6 +1261,10 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope =
|
||||
# Modules get an automatic title for the HTML, but no entry in the index.
|
||||
# better than `extractFilename(changeFileExt(d.filename, ""))` as it disambiguates dups
|
||||
title = $presentationPath(d.conf, AbsoluteFile d.filename, isTitle = true).changeFileExt("")
|
||||
var subtitle = "".rope
|
||||
if d.meta[metaSubtitle] != "":
|
||||
dispA(d.conf, subtitle, "<h2 class=\"subtitle\">$1</h2>",
|
||||
"\\\\\\vspace{0.5em}\\large $1", [d.meta[metaSubtitle].rope])
|
||||
|
||||
var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection")
|
||||
let bodyname = if d.hasToc and not d.isPureRst:
|
||||
@@ -1233,18 +1273,18 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope =
|
||||
elif d.hasToc: "doc.body_toc"
|
||||
else: "doc.body_no_toc"
|
||||
let seeSrcRope = genSeeSrcRope(d, d.filename, 1)
|
||||
content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title",
|
||||
content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", "subtitle",
|
||||
"tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref", "body_toc_groupsection", "seeSrc"],
|
||||
[title.rope, toc, d.modDesc, rope(getDateStr()),
|
||||
[title.rope, subtitle, toc, d.modDesc, rope(getDateStr()),
|
||||
rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile.AbsoluteFile, theindexFname.RelativeFile), groupsection.rope, seeSrcRope])
|
||||
if optCompileOnly notin d.conf.globalOptions:
|
||||
# XXX what is this hack doing here? 'optCompileOnly' means raw output!?
|
||||
code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [
|
||||
"nimdoccss", "dochackjs", "title", "tableofcontents", "moduledesc", "date", "time",
|
||||
"nimdoccss", "dochackjs", "title", "subtitle", "tableofcontents", "moduledesc", "date", "time",
|
||||
"content", "author", "version", "analytics", "deprecationMsg"],
|
||||
[relLink(d.conf.outDir, d.destFile.AbsoluteFile, nimdocOutCss.RelativeFile),
|
||||
relLink(d.conf.outDir, d.destFile.AbsoluteFile, docHackJsFname.RelativeFile),
|
||||
title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()),
|
||||
title.rope, subtitle, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()),
|
||||
content, d.meta[metaAuthor].rope, d.meta[metaVersion].rope, d.analytics.rope, d.modDeprecationMsg])
|
||||
else:
|
||||
code = content
|
||||
@@ -1331,8 +1371,9 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
|
||||
var d = newDocumentor(filen, cache, conf, outExt)
|
||||
|
||||
d.isPureRst = true
|
||||
var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
|
||||
{roSupportRawDirective, roSupportMarkdown}, conf)
|
||||
var rst = parseRst(readFile(filen.string), filen.string,
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
d.hasToc, {roSupportRawDirective, roSupportMarkdown}, conf)
|
||||
var modDesc = newStringOfCap(30_000)
|
||||
renderRstToOut(d[], rst, modDesc)
|
||||
d.modDesc = rope(modDesc)
|
||||
@@ -1396,11 +1437,11 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"")
|
||||
|
||||
let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), [
|
||||
"nimdoccss", "dochackjs",
|
||||
"title", "tableofcontents", "moduledesc", "date", "time",
|
||||
"title", "subtitle", "tableofcontents", "moduledesc", "date", "time",
|
||||
"content", "author", "version", "analytics"],
|
||||
[relLink(conf.outDir, filename, nimdocOutCss.RelativeFile),
|
||||
relLink(conf.outDir, filename, docHackJsFname.RelativeFile),
|
||||
rope"Index", nil, nil, rope(getDateStr()),
|
||||
rope"Index", rope"", nil, nil, rope(getDateStr()),
|
||||
rope(getClockStr()), content, nil, nil, nil])
|
||||
# no analytics because context is not available
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
import ast, idents, lineinfos, modulegraphs, magicsys
|
||||
|
||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), nextId idgen, t.owner, info)
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
dest.typ = t
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
||||
res.typ = getSysType(g, info, tyString)
|
||||
|
||||
result.typ = newType(tyProc, nextId idgen, t.owner)
|
||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
@@ -63,15 +63,15 @@ proc searchObjCase(t: PType; field: PSym): PNode =
|
||||
doAssert result != nil
|
||||
|
||||
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextId idgen, t.owner, info)
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
dest.typ = field.typ
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
||||
res.typ = getSysType(g, info, tyUInt8)
|
||||
|
||||
result.typ = newType(tyProc, nextId idgen, t.owner)
|
||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
|
||||
@@ -49,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
|
||||
var x = PSym(idTableGet(c.mapping, s))
|
||||
if x == nil:
|
||||
x = copySym(s, nextId(c.idgen))
|
||||
x = copySym(s, nextSymId(c.idgen))
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
x.owner = nil # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
@@ -187,7 +187,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
ctx.instID = instID[]
|
||||
ctx.idgen = idgen
|
||||
|
||||
let body = tmpl.getBody
|
||||
let body = tmpl.ast[bodyPos]
|
||||
#echo "instantion of ", renderTree(body, {renderIds})
|
||||
if isAtom(body):
|
||||
result = newNodeI(nkPar, body.info)
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
# from a lineinfos file, to provide generalized procedures to compile
|
||||
# nim files.
|
||||
|
||||
import
|
||||
ropes, os, strutils, osproc, platform, condsyms, options, msgs,
|
||||
lineinfos, std / sha1, streams, pathutils, sequtils, times, strtabs
|
||||
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils
|
||||
|
||||
import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json]
|
||||
|
||||
type
|
||||
TInfoCCProp* = enum # properties of the C compiler:
|
||||
@@ -124,8 +124,8 @@ compiler llvmGcc:
|
||||
result.name = "llvm_gcc"
|
||||
result.compilerExe = "llvm-gcc"
|
||||
result.cppCompiler = "llvm-g++"
|
||||
when defined(macosx):
|
||||
# OS X has no 'llvm-ar' tool:
|
||||
when defined(macosx) or defined(openbsd):
|
||||
# `llvm-ar` not available
|
||||
result.buildLib = "ar rcs $libfile $objfiles"
|
||||
else:
|
||||
result.buildLib = "llvm-ar rcs $libfile $objfiles"
|
||||
@@ -571,10 +571,10 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||
|
||||
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
|
||||
|
||||
var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
|
||||
let cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
|
||||
else: cfile.cname
|
||||
|
||||
var objfile =
|
||||
let objfile =
|
||||
if cfile.obj.isEmpty:
|
||||
if CfileFlag.External notin cfile.flags or noAbsolutePaths(conf):
|
||||
toObjFile(conf, cf).string
|
||||
@@ -629,8 +629,8 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
|
||||
proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
||||
if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good
|
||||
|
||||
var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
|
||||
var currentHash = footprint(conf, cfile)
|
||||
let hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
|
||||
let currentHash = footprint(conf, cfile)
|
||||
var f: File
|
||||
if open(f, hashFile.string, fmRead):
|
||||
let oldHash = parseSecureHash(f.readLine())
|
||||
@@ -662,6 +662,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
|
||||
proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
objfiles: string, isDllBuild: bool): string =
|
||||
if optGenStaticLib in conf.globalOptions:
|
||||
removeFile output # fixes: bug #16947
|
||||
result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
|
||||
"objfiles", objfiles]
|
||||
else:
|
||||
@@ -848,7 +849,10 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
var cmds: TStringSeq
|
||||
var prettyCmds: TStringSeq
|
||||
let prettyCb = proc (idx: int) =
|
||||
if prettyCmds[idx].len > 0: echo prettyCmds[idx]
|
||||
if prettyCmds[idx].len > 0:
|
||||
flushDot(conf)
|
||||
# xxx should probably use stderr like other compiler messages, not stdout
|
||||
echo prettyCmds[idx]
|
||||
|
||||
for idx, it in conf.toCompile:
|
||||
# call the C compiler for the .c file:
|
||||
@@ -899,8 +903,8 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
# only if not cached - copy the resulting main file from the nimcache folder to its originally intended destination
|
||||
if CfileFlag.Cached notin conf.toCompile[mainFileIdx].flags:
|
||||
let mainObjFile = getObjFilePath(conf, conf.toCompile[mainFileIdx])
|
||||
var src = conf.hcrLinkTargetName(mainObjFile, true)
|
||||
var dst = conf.prepareToWriteOutput
|
||||
let src = conf.hcrLinkTargetName(mainObjFile, true)
|
||||
let dst = conf.prepareToWriteOutput
|
||||
copyFileWithPermissions(src.string, dst.string)
|
||||
else:
|
||||
for x in conf.toCompile:
|
||||
@@ -929,20 +933,15 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
script.add("\n")
|
||||
generateScript(conf, script)
|
||||
|
||||
#from json import escapeJson
|
||||
import json, std / sha1
|
||||
|
||||
template hashNimExe(): string = $secureHashFile(os.getAppFilename())
|
||||
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
template lit(x: untyped) = f.write x
|
||||
template str(x: untyped) =
|
||||
when compiles(escapeJson(x, buf)):
|
||||
buf.setLen 0
|
||||
escapeJson(x, buf)
|
||||
f.write buf
|
||||
else:
|
||||
f.write escapeJson(x)
|
||||
template lit(x: string) = f.write x
|
||||
template str(x: string) =
|
||||
buf.setLen 0
|
||||
escapeJson(x, buf)
|
||||
f.write buf
|
||||
|
||||
proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
|
||||
var comma = false
|
||||
@@ -978,7 +977,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
pastStart = true
|
||||
lit "\L"
|
||||
|
||||
proc depfiles(conf: ConfigRef; f: File) =
|
||||
proc depfiles(conf: ConfigRef; f: File; buf: var string) =
|
||||
var i = 0
|
||||
for it in conf.m.fileInfos:
|
||||
let path = it.fullPath.string
|
||||
@@ -1023,12 +1022,14 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
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)
|
||||
depfiles(conf, f, buf)
|
||||
lit "],\L\"nimexe\": \L"
|
||||
str hashNimExe()
|
||||
lit "\L"
|
||||
@@ -1043,14 +1044,23 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol
|
||||
result = false
|
||||
try:
|
||||
let data = json.parseFile(jsonFile.string)
|
||||
if not data.hasKey("depfiles") or not data.hasKey("cmdline"):
|
||||
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
|
||||
if not data.hasKey("stdinInput"): return true
|
||||
let stdinInput = data["stdinInput"].getBool
|
||||
let projectIsCmd = data["projectIsCmd"].getBool
|
||||
if conf.projectIsStdin or stdinInput:
|
||||
|
||||
@@ -82,26 +82,6 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||
|
||||
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
||||
|
||||
type
|
||||
Operators* = object
|
||||
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
|
||||
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
|
||||
|
||||
proc initOperators*(g: ModuleGraph): Operators =
|
||||
result.opLe = createMagic(g, "<=", mLeI)
|
||||
result.opLt = createMagic(g, "<", mLtI)
|
||||
result.opAnd = createMagic(g, "and", mAnd)
|
||||
result.opOr = createMagic(g, "or", mOr)
|
||||
result.opIsNil = createMagic(g, "isnil", mIsNil)
|
||||
result.opEq = createMagic(g, "==", mEqI)
|
||||
result.opAdd = createMagic(g, "+", mAddI)
|
||||
result.opSub = createMagic(g, "-", mSubI)
|
||||
result.opMul = createMagic(g, "*", mMulI)
|
||||
result.opDiv = createMagic(g, "div", mDivI)
|
||||
result.opLen = createMagic(g, "len", mLengthSeq)
|
||||
result.opNot = createMagic(g, "not", mNot)
|
||||
result.opContains = createMagic(g, "contains", mInSet)
|
||||
|
||||
proc swapArgs(fact: PNode, newOp: PSym): PNode =
|
||||
result = newNodeI(nkCall, fact.info, 3)
|
||||
result[0] = newSymNode(newOp)
|
||||
@@ -404,16 +384,16 @@ proc usefulFact(n: PNode; o: Operators): PNode =
|
||||
type
|
||||
TModel* = object
|
||||
s*: seq[PNode] # the "knowledge base"
|
||||
o*: Operators
|
||||
g*: ModuleGraph
|
||||
beSmart*: bool
|
||||
|
||||
proc addFact*(m: var TModel, nn: PNode) =
|
||||
let n = usefulFact(nn, m.o)
|
||||
let n = usefulFact(nn, m.g.operators)
|
||||
if n != nil:
|
||||
if not m.beSmart:
|
||||
m.s.add n
|
||||
else:
|
||||
let c = canon(n, m.o)
|
||||
let c = canon(n, m.g.operators)
|
||||
if c.getMagic == mAnd:
|
||||
addFact(m, c[1])
|
||||
addFact(m, c[2])
|
||||
@@ -421,7 +401,7 @@ proc addFact*(m: var TModel, nn: PNode) =
|
||||
m.s.add c
|
||||
|
||||
proc addFactNeg*(m: var TModel, n: PNode) =
|
||||
let n = n.neg(m.o)
|
||||
let n = n.neg(m.g.operators)
|
||||
if n != nil: addFact(m, n)
|
||||
|
||||
proc sameOpr(a, b: PSym): bool =
|
||||
@@ -740,7 +720,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication =
|
||||
if result != impUnknown: return
|
||||
|
||||
proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
|
||||
result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
|
||||
result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators))
|
||||
|
||||
proc simpleSlice*(a, b: PNode): BiggestInt =
|
||||
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
|
||||
@@ -794,12 +774,7 @@ macro `=~`(x: PNode, pat: untyped): bool =
|
||||
|
||||
var conds = newTree(nnkBracket)
|
||||
m(x, pat, conds)
|
||||
when compiles(nestList(ident"and", conds)):
|
||||
result = nestList(ident"and", conds)
|
||||
#elif declared(macros.toNimIdent):
|
||||
# result = nestList(toNimIdent"and", conds)
|
||||
else:
|
||||
result = nestList(!"and", conds)
|
||||
result = nestList(ident"and", conds)
|
||||
|
||||
proc isMinusOne(n: PNode): bool =
|
||||
n.kind in {nkCharLit..nkUInt64Lit} and n.intVal == -1
|
||||
@@ -833,7 +808,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||
if b.getMagic in someAdd:
|
||||
if zero() <=? b[2] and a <=? b[1]: return impYes
|
||||
# x <= y-c if x+c <= y
|
||||
if b[2] <=? zero() and (canon(m.o.opSub.buildCall(a, b[2]), m.o) <=? b[1]):
|
||||
if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]):
|
||||
return impYes
|
||||
|
||||
# x+c <= y if c <= 0 and x <= y
|
||||
@@ -847,20 +822,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
|
||||
a[1][2].isValue:
|
||||
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
|
||||
if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
|
||||
if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
|
||||
return impYes
|
||||
|
||||
# x*3 + x == x*4. It follows that:
|
||||
# x*3 + y <= x*4 if y <= x and 3 <= 4
|
||||
if a =~ x*dc + y and b =~ x2*ec:
|
||||
if sameTree(x, x2):
|
||||
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
|
||||
let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
|
||||
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
|
||||
return impYes
|
||||
elif a =~ x*dc and b =~ x2*ec + y:
|
||||
#echo "BUG cam ehrer e ", a, " <=? ", b
|
||||
if sameTree(x, x2):
|
||||
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
|
||||
let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
|
||||
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
|
||||
return impYes
|
||||
|
||||
@@ -963,12 +938,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||
var b = bb
|
||||
if replacements.len > 0:
|
||||
m.s = @[]
|
||||
m.o = model.o
|
||||
m.g = model.g
|
||||
# make the other facts consistent:
|
||||
for fact in model.s:
|
||||
if fact != nil and fact.getMagic notin someEq:
|
||||
# XXX 'canon' should not be necessary here, but it is
|
||||
m.s.add applyReplacements(fact, replacements).canon(m.o)
|
||||
m.s.add applyReplacements(fact, replacements).canon(m.g.operators)
|
||||
a = applyReplacements(aa, replacements)
|
||||
b = applyReplacements(bb, replacements)
|
||||
else:
|
||||
@@ -977,19 +952,19 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||
result = pleViaModelRec(m, a, b)
|
||||
|
||||
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
||||
let x = canon(m.o.opLe.buildCall(a, b), m.o)
|
||||
let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
|
||||
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
|
||||
result = ple(m, x[1], x[2])
|
||||
if result == impUnknown:
|
||||
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
||||
let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
|
||||
let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators)
|
||||
result = ~ple(m, y[1], y[2])
|
||||
|
||||
proc addFactLe*(m: var TModel; a, b: PNode) =
|
||||
m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
|
||||
m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
|
||||
|
||||
proc addFactLt*(m: var TModel; a, b: PNode) =
|
||||
let bb = m.o.opAdd.buildCall(b, minusOne())
|
||||
let bb = m.g.operators.opAdd.buildCall(b, minusOne())
|
||||
addFactLe(m, a, bb)
|
||||
|
||||
proc settype(n: PNode): PType =
|
||||
@@ -1021,14 +996,14 @@ proc buildElse(n: PNode; o: Operators): PNode =
|
||||
|
||||
proc addDiscriminantFact*(m: var TModel, n: PNode) =
|
||||
var fact = newNodeI(nkCall, n.info, 3)
|
||||
fact[0] = newSymNode(m.o.opEq)
|
||||
fact[0] = newSymNode(m.g.operators.opEq)
|
||||
fact[1] = n[0]
|
||||
fact[2] = n[1]
|
||||
m.s.add fact
|
||||
|
||||
proc addAsgnFact*(m: var TModel, key, value: PNode) =
|
||||
var fact = newNodeI(nkCall, key.info, 3)
|
||||
fact[0] = newSymNode(m.o.opEq)
|
||||
fact[0] = newSymNode(m.g.operators.opEq)
|
||||
fact[1] = key
|
||||
fact[2] = value
|
||||
m.s.add fact
|
||||
@@ -1044,7 +1019,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||
# However, nil checking requires exactly the same mechanism! But for now
|
||||
# we simply use sameTree and live with the unsoundness of the analysis.
|
||||
var check = newNodeI(nkCall, a.info, 3)
|
||||
check[0] = newSymNode(m.o.opEq)
|
||||
check[0] = newSymNode(m.g.operators.opEq)
|
||||
check[1] = a
|
||||
check[2] = b
|
||||
result = m.doesImply(check) == impYes
|
||||
@@ -1052,9 +1027,9 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
|
||||
let branch = n[i]
|
||||
if branch.kind == nkOfBranch:
|
||||
m.s.add buildOf(branch, n[0], m.o)
|
||||
m.s.add buildOf(branch, n[0], m.g.operators)
|
||||
else:
|
||||
m.s.add n.buildElse(m.o).neg(m.o)
|
||||
m.s.add n.buildElse(m.g.operators).neg(m.g.operators)
|
||||
|
||||
proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
@@ -1072,6 +1047,6 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
|
||||
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
|
||||
for i in 1..<n.len:
|
||||
let check = buildProperFieldCheck(n[0], n[i], m.o)
|
||||
let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
|
||||
if check != nil and m.doesImply(check) != impYes:
|
||||
message(conf, n.info, warnProveField, renderTree(n[0])); break
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## A BiTable is a table that can be seen as an optimized pair
|
||||
## of (Table[LitId, Val], Table[Val, LitId]).
|
||||
|
||||
import hashes
|
||||
import hashes, rodfiles
|
||||
|
||||
type
|
||||
LitId* = distinct uint32
|
||||
@@ -30,7 +30,9 @@ proc mustRehash(length, counter: int): bool {.inline.} =
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
const
|
||||
idStart = 256 # Ids do not start with 0 but with this value. The IR needs it.
|
||||
idStart = 256 ##
|
||||
## Ids do not start with 0 but with this value. The IR needs it.
|
||||
## TODO: explain why
|
||||
|
||||
template idToIdx(x: LitId): int = x.int - idStart
|
||||
|
||||
@@ -94,6 +96,21 @@ proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} =
|
||||
assert idx < t.vals.len
|
||||
result = t.vals[idx]
|
||||
|
||||
proc hash*[T](t: BiTable[T]): Hash =
|
||||
## as the keys are hashes of the values, we simply use them instead
|
||||
var h: Hash = 0
|
||||
for i, n in pairs t.keys:
|
||||
h = h !& hash((i, n))
|
||||
result = !$h
|
||||
|
||||
proc store*[T](f: var RodFile; t: BiTable[T]) =
|
||||
storeSeq(f, t.vals)
|
||||
storeSeq(f, t.keys)
|
||||
|
||||
proc load*[T](f: var RodFile; t: var BiTable[T]) =
|
||||
loadSeq(f, t.vals)
|
||||
loadSeq(f, t.keys)
|
||||
|
||||
when isMainModule:
|
||||
|
||||
var t: BiTable[string]
|
||||
@@ -113,7 +130,35 @@ when isMainModule:
|
||||
|
||||
for i in 0 ..< 100_000:
|
||||
assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
|
||||
echo "begin"
|
||||
echo t.vals.len
|
||||
|
||||
echo t.vals[0]
|
||||
echo t.vals[1004]
|
||||
|
||||
echo "middle"
|
||||
|
||||
var tf: BiTable[float]
|
||||
|
||||
discard tf.getOrIncl(0.4)
|
||||
discard tf.getOrIncl(16.4)
|
||||
discard tf.getOrIncl(32.4)
|
||||
echo getKeyId(tf, 32.4)
|
||||
|
||||
var f2 = open("testblah.bin", fmWrite)
|
||||
echo store(f2, tf)
|
||||
f2.close
|
||||
|
||||
var f1 = open("testblah.bin", fmRead)
|
||||
|
||||
var t2: BiTable[float]
|
||||
|
||||
echo f1.load(t2)
|
||||
echo t2.vals.len
|
||||
|
||||
echo getKeyId(t2, 32.4)
|
||||
|
||||
echo "end"
|
||||
|
||||
|
||||
f1.close
|
||||
|
||||
106
compiler/ic/cbackend.nim
Normal file
106
compiler/ic/cbackend.nim
Normal file
@@ -0,0 +1,106 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2021 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## New entry point into our C/C++ code generator. Ideally
|
||||
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
|
||||
## to work on packed trees directly and produce the C code as an AST which can
|
||||
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
|
||||
## this code. So instead we wrap the existing cgen.nim and its friends so that
|
||||
## we call directly into the existing code generation logic but avoiding the
|
||||
## naive, outdated `passes` design. Thus you will see some
|
||||
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
|
||||
## also doing cross-module dependency tracking and DCE that we don't need
|
||||
## anymore. DCE is now done as prepass over the entire packed module graph.
|
||||
|
||||
import std/[packedsets, algorithm]
|
||||
# std/intsets would give `UnusedImport`, pending https://github.com/nim-lang/Nim/issues/14246
|
||||
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
pathutils, extccomp, msgs]
|
||||
|
||||
import packed_ast, ic, dce, rodfiles
|
||||
|
||||
proc unpackTree(g: ModuleGraph; thisModule: int;
|
||||
tree: PackedTree; n: NodePos): PNode =
|
||||
var decoder = initPackedDecoder(g.config, g.cache)
|
||||
result = loadNodes(decoder, g.packed, thisModule, tree, n)
|
||||
|
||||
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
|
||||
if g.backend == nil:
|
||||
g.backend = cgendata.newModuleList(g)
|
||||
|
||||
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
|
||||
bmod.idgen = idgenFromLoadedModule(m)
|
||||
bmod.flags.incl useAliveDataFromDce
|
||||
bmod.alive = move alive[m.module.position]
|
||||
|
||||
for p in allNodes(m.fromDisk.topLevel):
|
||||
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
|
||||
cgen.genTopLevelStmt(bmod, n)
|
||||
|
||||
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
|
||||
proc addFileToLink(config: ConfigRef; m: PSym) =
|
||||
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
|
||||
let ext =
|
||||
if config.backend == backendCpp: ".nim.cpp"
|
||||
elif config.backend == backendObjc: ".nim.m"
|
||||
else: ".nim.c"
|
||||
let cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext)
|
||||
let objFile = completeCfilePath(config, toObjFile(config, cfile))
|
||||
if fileExists(objFile):
|
||||
var cf = Cfile(nimname: m.name.s, cname: cfile,
|
||||
obj: objFile,
|
||||
flags: {CfileFlag.Cached})
|
||||
addFileToCompile(config, cf)
|
||||
|
||||
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
|
||||
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
|
||||
var s = newSeqOfCap[int32](alive[position].len)
|
||||
for a in items(alive[position]): s.add int32(a)
|
||||
sort(s)
|
||||
var f2 = rodfiles.open(asymFile.string)
|
||||
f2.loadHeader()
|
||||
f2.loadSection aliveSymsSection
|
||||
var oldData: seq[int32]
|
||||
f2.loadSeq(oldData)
|
||||
f2.close
|
||||
if f2.err == ok and oldData == s:
|
||||
result = false
|
||||
else:
|
||||
result = true
|
||||
var f = rodfiles.create(asymFile.string)
|
||||
f.storeHeader()
|
||||
f.storeSection aliveSymsSection
|
||||
f.storeSeq(s)
|
||||
close f
|
||||
|
||||
proc generateCode*(g: ModuleGraph) =
|
||||
## The single entry point, generate C(++) code for the entire
|
||||
## Nim program aka `ModuleGraph`.
|
||||
resetForBackend(g)
|
||||
var alive = computeAliveSyms(g.packed, g.config)
|
||||
|
||||
for i in 0..high(g.packed):
|
||||
# case statement here to enforce exhaustive checks.
|
||||
case g.packed[i].status
|
||||
of undefined:
|
||||
discard "nothing to do"
|
||||
of loading:
|
||||
assert false
|
||||
of storing, outdated:
|
||||
generateCodeForModule(g, g.packed[i], alive)
|
||||
of loaded:
|
||||
# Even though this module didn't change, DCE might trigger a change.
|
||||
# Consider this case: Module A uses symbol S from B and B does not use
|
||||
# S itself. A is then edited not to use S either. Thus we have to
|
||||
# recompile B in order to remove S from the final result.
|
||||
if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
|
||||
generateCodeForModule(g, g.packed[i], alive)
|
||||
else:
|
||||
addFileToLink(g.config, g.packed[i].module)
|
||||
161
compiler/ic/dce.nim
Normal file
161
compiler/ic/dce.nim
Normal file
@@ -0,0 +1,161 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2021 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Dead code elimination (=DCE) for IC.
|
||||
|
||||
import std / [intsets, tables]
|
||||
import ".." / [ast, options, lineinfos, types]
|
||||
|
||||
import packed_ast, ic, bitabs
|
||||
|
||||
type
|
||||
AliveSyms* = seq[IntSet]
|
||||
AliveContext* = object ## Purpose is to fill the 'alive' field.
|
||||
stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive.
|
||||
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
|
||||
thisModule: int ## The module we're currently analysing for DCE.
|
||||
alive: AliveSyms ## The final result of our computation.
|
||||
options: TOptions
|
||||
compilerProcs: Table[string, (int, int32)]
|
||||
|
||||
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
|
||||
## "Exported to C" procs are special (these are marked with '.exportc') because these
|
||||
## must not be optimized away!
|
||||
let symPtr = addr g[c.thisModule].fromDisk.sh.syms[symId]
|
||||
let flags = symPtr.flags
|
||||
# due to a bug/limitation in the lambda lifting, unused inner procs
|
||||
# are not transformed correctly; issue (#411). However, the whole purpose here
|
||||
# is to eliminate unused procs. So there is no special logic required for this case.
|
||||
if sfCompileTime notin flags:
|
||||
if ({sfExportc, sfCompilerProc} * flags != {}) or
|
||||
(symPtr.kind == skMethod):
|
||||
result = true
|
||||
# XXX: This used to be a condition to:
|
||||
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
|
||||
if sfCompilerProc in flags:
|
||||
c.compilerProcs[g[c.thisModule].fromDisk.sh.strings[symPtr.name]] = (c.thisModule, symId)
|
||||
|
||||
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
|
||||
|
||||
proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
|
||||
## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
|
||||
## be analysed.
|
||||
if not c.alive[module].containsOrIncl(item):
|
||||
var body = g[module].fromDisk.sh.syms[item].ast
|
||||
if body != emptyNodeId:
|
||||
let opt = g[module].fromDisk.sh.syms[item].options
|
||||
if g[module].fromDisk.sh.syms[item].kind in routineKinds:
|
||||
body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos)
|
||||
c.stack.add((module, opt, NodePos(body)))
|
||||
|
||||
when false:
|
||||
let nid = g[module].fromDisk.sh.syms[item].name
|
||||
if nid != LitId(0):
|
||||
let name = g[module].fromDisk.sh.strings[nid]
|
||||
if name in ["nimFrame", "callDepthLimitReached"]:
|
||||
echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item
|
||||
|
||||
proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) =
|
||||
let (module, item) = c.compilerProcs[name]
|
||||
followLater(c, g, module, item)
|
||||
|
||||
proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind =
|
||||
template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.sh.types[t.item].kind
|
||||
|
||||
var t2 = translateId(t, g, c.thisModule, c.decoder.config)
|
||||
result = t2.kind
|
||||
while result in toSkip:
|
||||
t2 = translateId(g[t2.module].fromDisk.sh.types[t2.item].types[^1], g, t2.module, c.decoder.config)
|
||||
result = t2.kind
|
||||
|
||||
proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
|
||||
## Replicates the logic of `ccgexprs.genRangeChck`.
|
||||
## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear
|
||||
## the approach has enough merit.
|
||||
var dest = loadTypeKind(n.typ, c, g, abstractVar)
|
||||
if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}:
|
||||
discard "no need to generate a check because it was disabled"
|
||||
else:
|
||||
let n0t = loadTypeKind(n.firstSon.typ, c, g, {})
|
||||
if n0t in {tyUInt, tyUInt64}:
|
||||
c.requestCompilerProc(g, "raiseRangeErrorNoArgs")
|
||||
else:
|
||||
let raiser =
|
||||
case loadTypeKind(n.typ, c, g, abstractVarRange)
|
||||
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
|
||||
of tyFloat..tyFloat128: "raiseRangeErrorF"
|
||||
else: "raiseRangeErrorI"
|
||||
c.requestCompilerProc(g, raiser)
|
||||
|
||||
proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
|
||||
## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
|
||||
## it is purely in a declarative context (type section etc.).
|
||||
case n.kind
|
||||
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
|
||||
discard "ignore non-sym atoms"
|
||||
of nkSym:
|
||||
# This symbol is alive and everything its body references.
|
||||
followLater(c, g, c.thisModule, n.operand)
|
||||
of nkModuleRef:
|
||||
let (n1, n2) = sons2(tree, n)
|
||||
assert n1.kind == nkInt32Lit
|
||||
assert n2.kind == nkInt32Lit
|
||||
let m = n1.litId
|
||||
let item = n2.operand
|
||||
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
|
||||
followLater(c, g, otherModule, item)
|
||||
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
|
||||
nkCommentStmt, nkIncludeStmt,
|
||||
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
||||
nkFromStmt, nkStaticStmt:
|
||||
discard
|
||||
of nkVarSection, nkLetSection, nkConstSection:
|
||||
# XXX ignore the defining local variable name?
|
||||
for son in sonsReadonly(tree, n):
|
||||
aliveCode(c, g, tree, son)
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
rangeCheckAnalysis(c, g, tree, n)
|
||||
of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
|
||||
if n.firstSon.kind == nkSym and isNotGeneric(n):
|
||||
let item = n.firstSon.operand
|
||||
if isExportedToC(c, g, item):
|
||||
# This symbol is alive and everything its body references.
|
||||
followLater(c, g, c.thisModule, item)
|
||||
else:
|
||||
for son in sonsReadonly(tree, n):
|
||||
aliveCode(c, g, tree, son)
|
||||
|
||||
proc followNow(c: var AliveContext; g: PackedModuleGraph) =
|
||||
## Mark all entries in the stack. Marking can add more entries
|
||||
## to the stack but eventually we have looked at every alive symbol.
|
||||
while c.stack.len > 0:
|
||||
let (modId, opt, ast) = c.stack.pop()
|
||||
c.thisModule = modId
|
||||
c.options = opt
|
||||
aliveCode(c, g, g[modId].fromDisk.bodies, ast)
|
||||
|
||||
proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
|
||||
## Entry point for our DCE algorithm.
|
||||
var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
|
||||
thisModule: -1, alive: newSeq[IntSet](g.len),
|
||||
options: conf.options)
|
||||
for i in countdown(high(g), 0):
|
||||
if g[i].status != undefined:
|
||||
c.thisModule = i
|
||||
for p in allNodes(g[i].fromDisk.topLevel):
|
||||
aliveCode(c, g, g[i].fromDisk.topLevel, p)
|
||||
|
||||
followNow(c, g)
|
||||
result = move(c.alive)
|
||||
|
||||
proc isAlive*(a: AliveSyms; module: int, item: int32): bool =
|
||||
## Backends use this to query if a symbol is `alive` which means
|
||||
## we need to produce (C/C++/etc) code for it.
|
||||
result = a[module].contains(item)
|
||||
|
||||
@@ -29,14 +29,32 @@ mechanism needs to be implemented that we could get wrong. ModuleIds
|
||||
are rod-file specific too.
|
||||
|
||||
|
||||
Configuration setup changes
|
||||
---------------------------
|
||||
|
||||
For a MVP these are not detected. Later the configuration will be
|
||||
stored in every `.rod` file.
|
||||
|
||||
|
||||
Global state
|
||||
------------
|
||||
|
||||
Global persistent state will be kept in a project specific `.rod` file.
|
||||
There is no global state.
|
||||
|
||||
Rod File Format
|
||||
---------------
|
||||
|
||||
It's a simple binary file format. `rodfiles.nim` contains some details.
|
||||
|
||||
|
||||
Backend
|
||||
-------
|
||||
|
||||
Nim programmers have to come to enjoy whole-program dead code elimination,
|
||||
by default. Since this is a "whole program" optimization, it does break
|
||||
modularity. However, thanks to the packed AST representation we can perform
|
||||
this global analysis without having to unpack anything. This is basically
|
||||
a mark&sweep GC algorithm:
|
||||
|
||||
- Start with the top level statements. Every symbol that is referenced
|
||||
from a top level statement is not "dead" and needs to be compiled by
|
||||
the backend.
|
||||
- Every symbol referenced from a referenced symbol also has to be
|
||||
compiled.
|
||||
|
||||
Caching logic: Only if the set of alive symbols is different from the
|
||||
last run, the module has to be regenerated.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import std / [hashes, tables]
|
||||
import bitabs
|
||||
import ".." / [ast, lineinfos, options, pathutils]
|
||||
1147
compiler/ic/ic.nim
Normal file
1147
compiler/ic/ic.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,43 +12,28 @@
|
||||
## use this representation directly in all the transformations,
|
||||
## it is superior.
|
||||
|
||||
import std / [hashes, tables]
|
||||
import std / [hashes, tables, strtabs]
|
||||
import bitabs
|
||||
import ".." / [ast, lineinfos, options, pathutils]
|
||||
|
||||
const
|
||||
localNamePos* = 0
|
||||
localExportMarkerPos* = 1
|
||||
localPragmaPos* = 2
|
||||
localTypePos* = 3
|
||||
localValuePos* = 4
|
||||
|
||||
typeNamePos* = 0
|
||||
typeExportMarkerPos* = 1
|
||||
typeGenericParamsPos* = 2
|
||||
typePragmaPos* = 3
|
||||
typeBodyPos* = 4
|
||||
|
||||
routineNamePos* = 0
|
||||
routineExportMarkerPos* = 1
|
||||
routinePatternPos* = 2
|
||||
routineGenericParamsPos* = 3
|
||||
routineParamsPos* = 4
|
||||
routineResultPos* = 5
|
||||
routinePragmasPos* = 6
|
||||
routineBodyPos* = 7
|
||||
|
||||
const
|
||||
nkModuleRef = nkNone # pair of (ModuleId, SymId)
|
||||
import ".." / [ast, options]
|
||||
|
||||
type
|
||||
SymId* = distinct int32
|
||||
TypeId* = distinct int32
|
||||
ModuleId* = distinct int32
|
||||
NodePos* = distinct int
|
||||
|
||||
NodeId* = distinct int32
|
||||
|
||||
PackedItemId* = object
|
||||
module*: LitId # 0 if it's this module
|
||||
item*: int32 # same as the in-memory representation
|
||||
|
||||
const
|
||||
nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
|
||||
|
||||
const
|
||||
emptyNodeId* = NodeId(-1)
|
||||
|
||||
type
|
||||
PackedLineInfo* = object
|
||||
line*: uint16
|
||||
col*: int16
|
||||
@@ -64,19 +49,20 @@ type
|
||||
PackedSym* = object
|
||||
kind*: TSymKind
|
||||
name*: LitId
|
||||
typeId*: TypeId
|
||||
typ*: PackedItemId
|
||||
flags*: TSymFlags
|
||||
magic*: TMagic
|
||||
info*: PackedLineInfo
|
||||
ast*: NodePos
|
||||
owner*: ItemId
|
||||
guard*: ItemId
|
||||
ast*: NodeId
|
||||
owner*: PackedItemId
|
||||
guard*: PackedItemId
|
||||
bitsize*: int
|
||||
alignment*: int # for alignment
|
||||
options*: TOptions
|
||||
position*: int
|
||||
offset*: int
|
||||
externalName*: LitId # instead of TLoc
|
||||
locFlags*: TLocFlags
|
||||
annex*: PackedLib
|
||||
when hasFFI:
|
||||
cname*: LitId
|
||||
@@ -84,103 +70,91 @@ type
|
||||
|
||||
PackedType* = object
|
||||
kind*: TTypeKind
|
||||
nodekind*: TNodeKind
|
||||
callConv*: TCallingConvention
|
||||
#nodekind*: TNodeKind
|
||||
flags*: TTypeFlags
|
||||
types*: int32
|
||||
nodes*: int32
|
||||
methods*: int32
|
||||
nodeflags*: TNodeFlags
|
||||
info*: PackedLineInfo
|
||||
sym*: ItemId
|
||||
owner*: ItemId
|
||||
attachedOps*: array[TTypeAttachedOp, ItemId]
|
||||
types*: seq[PackedItemId]
|
||||
n*: NodeId
|
||||
#nodeflags*: TNodeFlags
|
||||
sym*: PackedItemId
|
||||
owner*: PackedItemId
|
||||
size*: BiggestInt
|
||||
align*: int16
|
||||
paddingAtEnd*: int16
|
||||
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||
# not serialized: loc*: TLoc because it is backend-specific
|
||||
typeInst*: TypeId
|
||||
nonUniqueId*: ItemId
|
||||
typeInst*: PackedItemId
|
||||
nonUniqueId*: int32
|
||||
|
||||
Node* = object # 20 bytes
|
||||
PackedNode* = object # 20 bytes
|
||||
kind*: TNodeKind
|
||||
flags*: TNodeFlags
|
||||
operand*: int32 # for kind in {nkSym, nkSymDef}: SymId
|
||||
# for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
|
||||
# for kind in nkInt32Lit: direct value
|
||||
# for non-atom kinds: the number of nodes (for easy skipping)
|
||||
typeId*: TypeId
|
||||
typeId*: PackedItemId
|
||||
info*: PackedLineInfo
|
||||
|
||||
ModulePhase* = enum
|
||||
preLookup, lookedUpTopLevelStmts
|
||||
|
||||
Module* = object
|
||||
name*: string
|
||||
file*: AbsoluteFile
|
||||
ast*: PackedTree
|
||||
phase*: ModulePhase
|
||||
iface*: Table[string, seq[SymId]] # 'seq' because of overloading
|
||||
|
||||
Program* = ref object
|
||||
modules*: seq[Module]
|
||||
PackedTree* = object ## usually represents a full Nim module
|
||||
nodes*: seq[PackedNode]
|
||||
#sh*: Shared
|
||||
|
||||
Shared* = ref object # shared between different versions of 'Module'.
|
||||
# (though there is always exactly one valid
|
||||
# version of a module)
|
||||
syms*: seq[PackedSym]
|
||||
types*: seq[seq[Node]]
|
||||
types*: seq[PackedType]
|
||||
strings*: BiTable[string] # we could share these between modules.
|
||||
integers*: BiTable[BiggestInt]
|
||||
floats*: BiTable[BiggestFloat]
|
||||
config*: ConfigRef
|
||||
#thisModule*: ModuleId
|
||||
#program*: Program
|
||||
#config*: ConfigRef
|
||||
|
||||
PackedTree* = object ## usually represents a full Nim module
|
||||
nodes*: seq[Node]
|
||||
toPosition*: Table[SymId, NodePos]
|
||||
sh*: Shared
|
||||
PackedInstantiation* = object
|
||||
key*, sym*: PackedItemId
|
||||
concreteTypes*: seq[PackedItemId]
|
||||
|
||||
proc `==`*(a, b: SymId): bool {.borrow.}
|
||||
proc hash*(a: SymId): Hash {.borrow.}
|
||||
|
||||
proc `==`*(a, b: NodePos): bool {.borrow.}
|
||||
proc `==`*(a, b: TypeId): bool {.borrow.}
|
||||
proc `==`*(a, b: ModuleId): bool {.borrow.}
|
||||
|
||||
proc declareSym*(tree: var PackedTree; kind: TSymKind;
|
||||
name: LitId; info: PackedLineInfo): SymId =
|
||||
result = SymId(tree.sh.syms.len)
|
||||
tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
|
||||
#proc `==`*(a, b: PackedItemId): bool {.borrow.}
|
||||
proc `==`*(a, b: NodeId): bool {.borrow.}
|
||||
|
||||
proc newTreeFrom*(old: PackedTree): PackedTree =
|
||||
result.nodes = @[]
|
||||
result.sh = old.sh
|
||||
when false: result.sh = old.sh
|
||||
|
||||
proc litIdFromName*(tree: PackedTree; name: string): LitId =
|
||||
result = tree.sh.strings.getOrIncl(name)
|
||||
when false:
|
||||
proc declareSym*(tree: var PackedTree; kind: TSymKind;
|
||||
name: LitId; info: PackedLineInfo): SymId =
|
||||
result = SymId(tree.sh.syms.len)
|
||||
tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
|
||||
|
||||
proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
|
||||
tree.nodes.add Node(kind: kind, operand: int32 getOrIncl(tree.sh.strings, token), info: info)
|
||||
proc litIdFromName*(tree: PackedTree; name: string): LitId =
|
||||
result = tree.sh.strings.getOrIncl(name)
|
||||
|
||||
proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
|
||||
tree.nodes.add Node(kind: kind, operand: 0, info: info)
|
||||
proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
|
||||
tree.nodes.add PackedNode(kind: kind, info: info,
|
||||
operand: int32 getOrIncl(tree.sh.strings, token))
|
||||
|
||||
proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
|
||||
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
|
||||
|
||||
proc throwAwayLastNode*(tree: var PackedTree) =
|
||||
tree.nodes.setLen(tree.nodes.len-1)
|
||||
|
||||
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
|
||||
tree.nodes.add Node(kind: nkIdent, operand: int32(s), info: info)
|
||||
tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
|
||||
|
||||
proc addSym*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
|
||||
tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info)
|
||||
proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
|
||||
tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
|
||||
|
||||
proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
|
||||
tree.nodes.add Node(kind: nkInt32Lit, operand: int32(s), info: info)
|
||||
tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
|
||||
|
||||
proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
|
||||
tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info)
|
||||
tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
|
||||
|
||||
proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
|
||||
|
||||
@@ -194,11 +168,12 @@ proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
|
||||
for i in 0..<L:
|
||||
dest.nodes[d+i] = tree.nodes[pos+i]
|
||||
|
||||
proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
|
||||
result = SymId(dest.sh.syms.len)
|
||||
assert int(s) < tree.sh.syms.len
|
||||
let oldSym = tree.sh.syms[s.int]
|
||||
dest.sh.syms.add oldSym
|
||||
when false:
|
||||
proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
|
||||
result = SymId(dest.sh.syms.len)
|
||||
assert int(s) < tree.sh.syms.len
|
||||
let oldSym = tree.sh.syms[s.int]
|
||||
dest.sh.syms.add oldSym
|
||||
|
||||
type
|
||||
PatchPos = distinct int
|
||||
@@ -206,11 +181,12 @@ type
|
||||
when false:
|
||||
proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
|
||||
result = PatchPos tree.nodes.len
|
||||
tree.nodes.add Node(kind: kind, operand: 0, info: info)
|
||||
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
|
||||
|
||||
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: TypeId; info: PackedLineInfo): PatchPos =
|
||||
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
|
||||
result = PatchPos tree.nodes.len
|
||||
tree.nodes.add Node(kind: kind, flags: flags, operand: 0, typeId: typeId, info: info)
|
||||
tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info,
|
||||
typeId: typeId)
|
||||
|
||||
proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
|
||||
result = PatchPos dest.nodes.len
|
||||
@@ -224,7 +200,8 @@ proc patch*(tree: var PackedTree; pos: PatchPos) =
|
||||
|
||||
proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
|
||||
|
||||
proc `[]`*(tree: PackedTree; i: int): lent Node {.inline.} = tree.nodes[i]
|
||||
proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
|
||||
tree.nodes[i]
|
||||
|
||||
proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
|
||||
if tree.nodes[pos].kind > nkNilLit:
|
||||
@@ -247,7 +224,8 @@ iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
|
||||
for x in sonsReadonly(tree, n): yield x
|
||||
patch dest, patchPos
|
||||
|
||||
iterator isons*(dest: var PackedTree; tree: PackedTree; n: NodePos): (int, NodePos) =
|
||||
iterator isons*(dest: var PackedTree; tree: PackedTree;
|
||||
n: NodePos): (int, NodePos) =
|
||||
var i = 0
|
||||
for ch0 in sons(dest, tree, n):
|
||||
yield (i, ch0)
|
||||
@@ -301,12 +279,24 @@ proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
|
||||
if count >= x: return true
|
||||
return false
|
||||
|
||||
proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
|
||||
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} = tree.nodes[n.int].kind
|
||||
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} = LitId tree.nodes[n.int].operand
|
||||
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} = tree.nodes[n.int].info
|
||||
proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
|
||||
NodePos(n.int+1)
|
||||
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
|
||||
tree.nodes[n.int].kind
|
||||
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
|
||||
LitId tree.nodes[n.int].operand
|
||||
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
|
||||
tree.nodes[n.int].info
|
||||
|
||||
proc span(tree: PackedTree; pos: int): int {.inline.} =
|
||||
template typ*(n: NodePos): PackedItemId =
|
||||
tree.nodes[n.int].typeId
|
||||
template flags*(n: NodePos): TNodeFlags =
|
||||
tree.nodes[n.int].flags
|
||||
|
||||
template operand*(n: NodePos): int32 =
|
||||
tree.nodes[n.int].operand
|
||||
|
||||
proc span*(tree: PackedTree; pos: int): int {.inline.} =
|
||||
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
|
||||
|
||||
proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
|
||||
@@ -330,7 +320,9 @@ proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
|
||||
inc count
|
||||
assert false, "node has no i-th child"
|
||||
|
||||
proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} = tree.sh.strings[lit]
|
||||
when false:
|
||||
proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} =
|
||||
tree.sh.strings[lit]
|
||||
|
||||
template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
|
||||
template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
|
||||
@@ -340,29 +332,30 @@ template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand
|
||||
|
||||
proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
|
||||
|
||||
proc strLit*(tree: PackedTree; n: NodePos): lent string =
|
||||
assert n.kind == nkStrLit
|
||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
||||
when false:
|
||||
proc strLit*(tree: PackedTree; n: NodePos): lent string =
|
||||
assert n.kind == nkStrLit
|
||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
||||
|
||||
proc strVal*(tree: PackedTree; n: NodePos): string =
|
||||
assert n.kind == nkStrLit
|
||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
||||
#result = cookedStrLit(raw)
|
||||
proc strVal*(tree: PackedTree; n: NodePos): string =
|
||||
assert n.kind == nkStrLit
|
||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
||||
#result = cookedStrLit(raw)
|
||||
|
||||
proc filenameVal*(tree: PackedTree; n: NodePos): string =
|
||||
case n.kind
|
||||
of nkStrLit:
|
||||
result = strVal(tree, n)
|
||||
of nkIdent:
|
||||
result = tree.sh.strings[n.litId]
|
||||
of nkSym:
|
||||
result = tree.sh.strings[tree.sh.syms[int n.symId].name]
|
||||
else:
|
||||
result = ""
|
||||
proc filenameVal*(tree: PackedTree; n: NodePos): string =
|
||||
case n.kind
|
||||
of nkStrLit:
|
||||
result = strVal(tree, n)
|
||||
of nkIdent:
|
||||
result = tree.sh.strings[n.litId]
|
||||
of nkSym:
|
||||
result = tree.sh.strings[tree.sh.syms[int n.symId].name]
|
||||
else:
|
||||
result = ""
|
||||
|
||||
proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
|
||||
assert n.kind == nkIdent
|
||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
||||
proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
|
||||
assert n.kind == nkIdent
|
||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
||||
|
||||
const
|
||||
externIntLit* = {nkCharLit,
|
||||
@@ -380,7 +373,8 @@ const
|
||||
externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
|
||||
directIntLit* = nkInt32Lit
|
||||
|
||||
proc toString*(tree: PackedTree; n: NodePos; nesting: int; result: var string) =
|
||||
proc toString*(tree: PackedTree; n: NodePos; sh: Shared; nesting: int;
|
||||
result: var string) =
|
||||
let pos = n.int
|
||||
if result.len > 0 and result[^1] notin {' ', '\n'}:
|
||||
result.add ' '
|
||||
@@ -390,46 +384,47 @@ proc toString*(tree: PackedTree; n: NodePos; nesting: int; result: var string) =
|
||||
of nkNone, nkEmpty, nkNilLit, nkType: discard
|
||||
of nkIdent, nkStrLit..nkTripleStrLit:
|
||||
result.add " "
|
||||
result.add tree.sh.strings[LitId tree.nodes[pos].operand]
|
||||
result.add sh.strings[LitId tree.nodes[pos].operand]
|
||||
of nkSym:
|
||||
result.add " "
|
||||
result.add tree.sh.strings[tree.sh.syms[tree.nodes[pos].operand].name]
|
||||
result.add sh.strings[sh.syms[tree.nodes[pos].operand].name]
|
||||
of directIntLit:
|
||||
result.add " "
|
||||
result.addInt tree.nodes[pos].operand
|
||||
of externSIntLit:
|
||||
result.add " "
|
||||
result.addInt tree.sh.integers[LitId tree.nodes[pos].operand]
|
||||
result.addInt sh.integers[LitId tree.nodes[pos].operand]
|
||||
of externUIntLit:
|
||||
result.add " "
|
||||
result.add $cast[uint64](tree.sh.integers[LitId tree.nodes[pos].operand])
|
||||
result.add $cast[uint64](sh.integers[LitId tree.nodes[pos].operand])
|
||||
else:
|
||||
result.add "(\n"
|
||||
for i in 1..(nesting+1)*2: result.add ' '
|
||||
for child in sonsReadonly(tree, n):
|
||||
toString(tree, child, nesting + 1, result)
|
||||
toString(tree, child, sh, nesting + 1, result)
|
||||
result.add "\n"
|
||||
for i in 1..nesting*2: result.add ' '
|
||||
result.add ")"
|
||||
#for i in 1..nesting*2: result.add ' '
|
||||
|
||||
|
||||
proc toString*(tree: PackedTree; n: NodePos): string =
|
||||
proc toString*(tree: PackedTree; n: NodePos; sh: Shared): string =
|
||||
result = ""
|
||||
toString(tree, n, 0, result)
|
||||
toString(tree, n, sh, 0, result)
|
||||
|
||||
proc debug*(tree: PackedTree) =
|
||||
stdout.write toString(tree, NodePos 0)
|
||||
proc debug*(tree: PackedTree; sh: Shared) =
|
||||
stdout.write toString(tree, NodePos 0, sh)
|
||||
|
||||
proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
|
||||
if n.kind == nkIdent:
|
||||
result = n.litId
|
||||
elif n.kind == nkSym:
|
||||
result = tree.sh.syms[int n.symId].name
|
||||
else:
|
||||
result = LitId(0)
|
||||
when false:
|
||||
proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
|
||||
if n.kind == nkIdent:
|
||||
result = n.litId
|
||||
elif n.kind == nkSym:
|
||||
result = tree.sh.syms[int n.symId].name
|
||||
else:
|
||||
result = LitId(0)
|
||||
|
||||
template identId*(n: NodePos): LitId = identIdImpl(tree, n)
|
||||
template identId*(n: NodePos): LitId = identIdImpl(tree, n)
|
||||
|
||||
template copyInto*(dest, n, body) =
|
||||
let patchPos = prepare(dest, tree, n)
|
||||
@@ -441,17 +436,20 @@ template copyIntoKind*(dest, kind, info, body) =
|
||||
body
|
||||
patch dest, patchPos
|
||||
|
||||
proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
|
||||
let litId = tree.sh.strings.getKeyId(pragma)
|
||||
if litId == LitId(0):
|
||||
return false
|
||||
assert n.kind == nkPragma
|
||||
for ch0 in sonsReadonly(tree, n):
|
||||
if ch0.kind == nkExprColonExpr:
|
||||
if ch0.firstSon.identId == litId:
|
||||
when false:
|
||||
proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
|
||||
let litId = tree.sh.strings.getKeyId(pragma)
|
||||
if litId == LitId(0):
|
||||
return false
|
||||
assert n.kind == nkPragma
|
||||
for ch0 in sonsReadonly(tree, n):
|
||||
if ch0.kind == nkExprColonExpr:
|
||||
if ch0.firstSon.identId == litId:
|
||||
return true
|
||||
elif ch0.identId == litId:
|
||||
return true
|
||||
elif ch0.identId == litId:
|
||||
return true
|
||||
|
||||
proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
|
||||
|
||||
when false:
|
||||
proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
|
||||
@@ -459,3 +457,13 @@ when false:
|
||||
dest.add nkStrLit, msg, n.info
|
||||
copyTree(dest, tree, n)
|
||||
patch dest, patchPos
|
||||
|
||||
iterator allNodes*(tree: PackedTree): NodePos =
|
||||
var p = 0
|
||||
while p < tree.len:
|
||||
yield NodePos(p)
|
||||
let s = span(tree, p)
|
||||
inc p, s
|
||||
|
||||
proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
|
||||
PackedItemId(module: LitId(0), item: item)
|
||||
|
||||
147
compiler/ic/replayer.nim
Normal file
147
compiler/ic/replayer.nim
Normal file
@@ -0,0 +1,147 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Module that contains code to replay global VM state changes and pragma
|
||||
## state like ``{.compile: "foo.c".}``. For IC (= Incremental compilation)
|
||||
## support.
|
||||
|
||||
import ".." / [ast, modulegraphs, trees, extccomp, btrees,
|
||||
msgs, lineinfos, pathutils, options, cgmeth]
|
||||
|
||||
import tables
|
||||
|
||||
import packed_ast, ic, bitabs
|
||||
|
||||
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
||||
let list = module.ast
|
||||
assert list != nil
|
||||
assert list.kind == nkStmtList
|
||||
for n in list:
|
||||
assert n.kind == nkReplayAction
|
||||
# Fortunately only a tiny subset of the available pragmas need to
|
||||
# be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
|
||||
if n.len >= 2:
|
||||
internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
|
||||
case n[0].strVal
|
||||
of "hint": message(g.config, n.info, hintUser, n[1].strVal)
|
||||
of "warning": message(g.config, n.info, warnUser, n[1].strVal)
|
||||
of "error": localError(g.config, n.info, errUser, n[1].strVal)
|
||||
of "compile":
|
||||
internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit
|
||||
let cname = AbsoluteFile n[1].strVal
|
||||
var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
|
||||
obj: AbsoluteFile n[2].strVal,
|
||||
flags: {CfileFlag.External},
|
||||
customArgs: n[3].strVal)
|
||||
extccomp.addExternalFileToCompile(g.config, cf)
|
||||
of "link":
|
||||
extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
|
||||
of "passl":
|
||||
extccomp.addLinkOption(g.config, n[1].strVal)
|
||||
of "passc":
|
||||
extccomp.addCompileOption(g.config, n[1].strVal)
|
||||
of "localpassc":
|
||||
extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex))
|
||||
of "cppdefine":
|
||||
options.cppDefine(g.config, n[1].strVal)
|
||||
of "inc":
|
||||
let destKey = n[1].strVal
|
||||
let by = n[2].intVal
|
||||
let v = getOrDefault(g.cacheCounters, destKey)
|
||||
g.cacheCounters[destKey] = v+by
|
||||
of "put":
|
||||
let destKey = n[1].strVal
|
||||
let key = n[2].strVal
|
||||
let val = n[3]
|
||||
if not contains(g.cacheTables, destKey):
|
||||
g.cacheTables[destKey] = initBTree[string, PNode]()
|
||||
if not contains(g.cacheTables[destKey], key):
|
||||
g.cacheTables[destKey].add(key, val)
|
||||
else:
|
||||
internalError(g.config, n.info, "key already exists: " & key)
|
||||
of "incl":
|
||||
let destKey = n[1].strVal
|
||||
let val = n[2]
|
||||
if not contains(g.cacheSeqs, destKey):
|
||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
||||
else:
|
||||
block search:
|
||||
for existing in g.cacheSeqs[destKey]:
|
||||
if exprStructuralEquivalent(existing, val, strictSymEquality=true):
|
||||
break search
|
||||
g.cacheSeqs[destKey].add val
|
||||
of "add":
|
||||
let destKey = n[1].strVal
|
||||
let val = n[2]
|
||||
if not contains(g.cacheSeqs, destKey):
|
||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
||||
else:
|
||||
g.cacheSeqs[destKey].add val
|
||||
else:
|
||||
internalAssert g.config, false
|
||||
|
||||
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
||||
## We remember the generic instantiations a module performed
|
||||
## in order to to avoid the code bloat that generic code tends
|
||||
## to imply. This is cheaper than deduplication of identical
|
||||
## generic instantiations. However, deduplication is more
|
||||
## powerful and general and I hope to implement it soon too
|
||||
## (famous last words).
|
||||
assert g.packed[module].status == loaded
|
||||
for it in g.packed[module].fromDisk.typeInstCache:
|
||||
let key = translateId(it[0], g.packed, module, g.config)
|
||||
g.typeInstCache.mgetOrPut(key, @[]).add LazyType(id: FullId(module: module, packed: it[1]), typ: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.procInstCache):
|
||||
let key = translateId(it.key, g.packed, module, g.config)
|
||||
let sym = translateId(it.sym, g.packed, module, g.config)
|
||||
var concreteTypes = newSeq[FullId](it.concreteTypes.len)
|
||||
for i in 0..high(it.concreteTypes):
|
||||
let tmp = translateId(it.concreteTypes[i], g.packed, module, g.config)
|
||||
concreteTypes[i] = FullId(module: tmp.module, packed: it.concreteTypes[i])
|
||||
|
||||
g.procInstCache.mgetOrPut(key, @[]).add LazyInstantiation(
|
||||
module: module, sym: FullId(module: sym.module, packed: it.sym),
|
||||
concreteTypes: concreteTypes, inst: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.methodsPerType):
|
||||
let key = translateId(it[0], g.packed, module, g.config)
|
||||
let col = it[1]
|
||||
let tmp = translateId(it[2], g.packed, module, g.config)
|
||||
let symId = FullId(module: tmp.module, packed: it[2])
|
||||
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
|
||||
let key = translateId(it[0], g.packed, module, g.config)
|
||||
let tmp = translateId(it[1], g.packed, module, g.config)
|
||||
let symId = FullId(module: tmp.module, packed: it[1])
|
||||
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.methods):
|
||||
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
|
||||
PackedItemId(module: LitId(0), item: it))
|
||||
methodDef(g, g.idgen, sym)
|
||||
|
||||
when false:
|
||||
# not used anymore:
|
||||
for it in mitems(g.packed[module].fromDisk.compilerProcs):
|
||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1]))
|
||||
g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.converters):
|
||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
|
||||
g.ifaces[module].converters.add LazySym(id: symId, sym: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.trmacros):
|
||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
|
||||
g.ifaces[module].patterns.add LazySym(id: symId, sym: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.pureEnums):
|
||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
|
||||
g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil)
|
||||
174
compiler/ic/rodfiles.nim
Normal file
174
compiler/ic/rodfiles.nim
Normal file
@@ -0,0 +1,174 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
from typetraits import supportsCopyMem
|
||||
|
||||
type
|
||||
RodSection* = enum
|
||||
versionSection
|
||||
configSection
|
||||
stringsSection
|
||||
checkSumsSection
|
||||
depsSection
|
||||
integersSection
|
||||
floatsSection
|
||||
exportsSection
|
||||
reexportsSection
|
||||
compilerProcsSection
|
||||
trmacrosSection
|
||||
convertersSection
|
||||
methodsSection
|
||||
pureEnumsSection
|
||||
macroUsagesSection
|
||||
toReplaySection
|
||||
topLevelSection
|
||||
bodiesSection
|
||||
symsSection
|
||||
typesSection
|
||||
typeInstCacheSection
|
||||
procInstCacheSection
|
||||
attachedOpsSection
|
||||
methodsPerTypeSection
|
||||
enumToStringProcsSection
|
||||
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
|
||||
|
||||
RodFileError* = enum
|
||||
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
|
||||
includeFileChanged
|
||||
|
||||
RodFile* = object
|
||||
f*: File
|
||||
currentSection*: RodSection # for error checking
|
||||
err*: RodFileError # little experiment to see if this works
|
||||
# better than exceptions.
|
||||
|
||||
const
|
||||
RodVersion = 1
|
||||
cookie = [byte(0), byte('R'), byte('O'), byte('D'),
|
||||
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
|
||||
|
||||
proc setError(f: var RodFile; err: RodFileError) {.inline.} =
|
||||
f.err = err
|
||||
#raise newException(IOError, "IO error")
|
||||
|
||||
proc storePrim*(f: var RodFile; s: string) =
|
||||
if f.err != ok: return
|
||||
if s.len >= high(int32):
|
||||
setError f, tooBig
|
||||
return
|
||||
var lenPrefix = int32(s.len)
|
||||
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
||||
setError f, ioFailure
|
||||
else:
|
||||
if s.len != 0:
|
||||
if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
|
||||
setError f, ioFailure
|
||||
|
||||
proc storePrim*[T](f: var RodFile; x: T) =
|
||||
if f.err != ok: return
|
||||
when supportsCopyMem(T):
|
||||
if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
|
||||
setError f, ioFailure
|
||||
elif T is tuple:
|
||||
for y in fields(x):
|
||||
storePrim(f, y)
|
||||
elif T is object:
|
||||
for y in fields(x):
|
||||
when y is seq:
|
||||
storeSeq(f, y)
|
||||
else:
|
||||
storePrim(f, y)
|
||||
else:
|
||||
{.error: "unsupported type for 'storePrim'".}
|
||||
|
||||
proc storeSeq*[T](f: var RodFile; s: seq[T]) =
|
||||
if f.err != ok: return
|
||||
if s.len >= high(int32):
|
||||
setError f, tooBig
|
||||
return
|
||||
var lenPrefix = int32(s.len)
|
||||
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
||||
setError f, ioFailure
|
||||
else:
|
||||
for i in 0..<s.len:
|
||||
storePrim(f, s[i])
|
||||
|
||||
proc loadPrim*(f: var RodFile; s: var string) =
|
||||
if f.err != ok: return
|
||||
var lenPrefix = int32(0)
|
||||
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
||||
setError f, ioFailure
|
||||
else:
|
||||
s = newString(lenPrefix)
|
||||
if lenPrefix > 0:
|
||||
if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
|
||||
setError f, ioFailure
|
||||
|
||||
proc loadPrim*[T](f: var RodFile; x: var T) =
|
||||
if f.err != ok: return
|
||||
when supportsCopyMem(T):
|
||||
if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
|
||||
setError f, ioFailure
|
||||
elif T is tuple:
|
||||
for y in fields(x):
|
||||
loadPrim(f, y)
|
||||
elif T is object:
|
||||
for y in fields(x):
|
||||
when y is seq:
|
||||
loadSeq(f, y)
|
||||
else:
|
||||
loadPrim(f, y)
|
||||
else:
|
||||
{.error: "unsupported type for 'loadPrim'".}
|
||||
|
||||
proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
|
||||
if f.err != ok: return
|
||||
var lenPrefix = int32(0)
|
||||
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
||||
setError f, ioFailure
|
||||
else:
|
||||
s = newSeq[T](lenPrefix)
|
||||
for i in 0..<lenPrefix:
|
||||
loadPrim(f, s[i])
|
||||
|
||||
proc storeHeader*(f: var RodFile) =
|
||||
if f.err != ok: return
|
||||
if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
|
||||
setError f, ioFailure
|
||||
|
||||
proc loadHeader*(f: var RodFile) =
|
||||
if f.err != ok: return
|
||||
var thisCookie: array[cookie.len, byte]
|
||||
if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
|
||||
setError f, ioFailure
|
||||
elif thisCookie != cookie:
|
||||
setError f, wrongHeader
|
||||
|
||||
proc storeSection*(f: var RodFile; s: RodSection) =
|
||||
if f.err != ok: return
|
||||
assert f.currentSection < s
|
||||
f.currentSection = s
|
||||
storePrim(f, s)
|
||||
|
||||
proc loadSection*(f: var RodFile; expected: RodSection) =
|
||||
if f.err != ok: return
|
||||
var s: RodSection
|
||||
loadPrim(f, s)
|
||||
if expected != s and f.err == ok:
|
||||
setError f, wrongSection
|
||||
|
||||
proc create*(filename: string): RodFile =
|
||||
if not open(result.f, filename, fmWrite):
|
||||
setError result, cannotOpen
|
||||
|
||||
proc close*(f: var RodFile) = close(f.f)
|
||||
|
||||
proc open*(filename: string): RodFile =
|
||||
if not open(result.f, filename, fmRead):
|
||||
setError result, cannotOpen
|
||||
@@ -1,90 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import std / [hashes, tables]
|
||||
import packed_ast, bitabs
|
||||
import ".." / [ast, idents, lineinfos, options, pathutils, msgs]
|
||||
|
||||
type
|
||||
Context = object
|
||||
thisModule: int32
|
||||
lastFile: FileIndex # remember the last lookup entry.
|
||||
lastLit: LitId
|
||||
filenames: Table[FileIndex, LitId]
|
||||
|
||||
proc toLitId(x: FileIndex; ir: var PackedTree; c: var Context): LitId =
|
||||
if x == c.lastFile:
|
||||
result = c.lastLit
|
||||
else:
|
||||
result = c.filenames.getOrDefault(x)
|
||||
if result == LitId(0):
|
||||
let p = msgs.toFullPath(ir.sh.config, x)
|
||||
result = getOrIncl(ir.sh.strings, p)
|
||||
c.filenames[x] = result
|
||||
c.lastFile = x
|
||||
c.lastLit = result
|
||||
|
||||
proc toPackedInfo(x: TLineInfo; ir: var PackedTree; c: var Context): PackedLineInfo =
|
||||
PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, ir, c))
|
||||
|
||||
proc toPackedType(t: PType; ir: var PackedTree; c: var Context): TypeId =
|
||||
result = TypeId(0)
|
||||
|
||||
proc toPackedSym(s: PSym; ir: var PackedTree; c: var Context): SymId =
|
||||
result = SymId(0)
|
||||
|
||||
proc toPackedSymNode(n: PNode; ir: var PackedTree; c: var Context) =
|
||||
assert n.kind == nkSym
|
||||
let t = toPackedType(n.typ, ir, c)
|
||||
|
||||
if n.sym.itemId.module == c.thisModule:
|
||||
# it is a symbol that belongs to the module we're currently
|
||||
# packing:
|
||||
let sid = toPackedSym(n.sym, ir, c)
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(sid),
|
||||
typeId: t, info: toPackedInfo(n.info, ir, c))
|
||||
else:
|
||||
# store it as an external module reference:
|
||||
# nkModuleRef
|
||||
discard
|
||||
|
||||
|
||||
proc toPackedNode*(n: PNode; ir: var PackedTree; c: var Context) =
|
||||
template toP(x: TLineInfo): PackedLineInfo = toPackedInfo(x, ir, c)
|
||||
|
||||
case n.kind
|
||||
of nkNone, nkEmpty, nkNilLit:
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: 0,
|
||||
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
|
||||
of nkIdent:
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.ident.s),
|
||||
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
|
||||
of nkSym:
|
||||
toPackedSymNode(n, ir, c)
|
||||
of directIntLit:
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(n.intVal),
|
||||
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
|
||||
of externIntLit:
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.integers, n.intVal),
|
||||
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.strVal),
|
||||
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.floats, n.floatVal),
|
||||
typeId: toPackedType(n.typ, ir, c), info: toP n.info)
|
||||
else:
|
||||
let patchPos = ir.prepare(n.kind, n.flags, toPackedType(n.typ, ir, c), toP n.info)
|
||||
for i in 0..<n.len:
|
||||
toPackedNode(n[i], ir, c)
|
||||
ir.patch patchPos
|
||||
|
||||
proc moduleToIr*(n: PNode; ir: var PackedTree; module: PSym) =
|
||||
var c = Context(thisModule: module.itemId.module)
|
||||
toPackedNode(n, ir, c)
|
||||
@@ -113,3 +113,8 @@ proc newIdentCache*(): IdentCache =
|
||||
proc whichKeyword*(id: PIdent): TSpecialWord =
|
||||
if id.id < 0: result = wInvalid
|
||||
else: result = TSpecialWord(id.id)
|
||||
|
||||
proc hash*(x: PIdent): Hash {.inline.} = x.h
|
||||
proc `==`*(a, b: PIdent): bool {.inline.} =
|
||||
if a.isNil or b.isNil: result = system.`==`(a, b)
|
||||
else: result = a.id == b.id
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
|
||||
import
|
||||
intsets, ast, astalgo, msgs, options, idents, lookups,
|
||||
semdata, modulepaths, sigmatch, lineinfos, sets
|
||||
semdata, modulepaths, sigmatch, lineinfos, sets,
|
||||
modulegraphs
|
||||
|
||||
proc readExceptSet*(c: PContext, n: PNode): IntSet =
|
||||
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
|
||||
@@ -101,14 +102,14 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
|
||||
else:
|
||||
importPureEnumField(c, e)
|
||||
else:
|
||||
if s.kind == skConverter: addConverter(c, s)
|
||||
if hasPattern(s): addPattern(c, s)
|
||||
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
|
||||
if hasPattern(s): addPattern(c, LazySym(sym: s))
|
||||
if s.owner != origin:
|
||||
c.exportIndirections.incl((origin.id, s.id))
|
||||
|
||||
proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
||||
let ident = lookups.considerQuotedIdent(c, n)
|
||||
let s = strTableGet(fromMod.tab, ident)
|
||||
let s = someSym(c.graph, fromMod, ident)
|
||||
if s == nil:
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
||||
else:
|
||||
@@ -118,16 +119,16 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
||||
# for an enumeration we have to add all identifiers
|
||||
if multiImport:
|
||||
# for a overloadable syms add all overloaded routines
|
||||
var it: TIdentIter
|
||||
var e = initIdentIter(it, fromMod.tab, s.name)
|
||||
var it: ModuleIter
|
||||
var e = initModuleIter(it, c.graph, fromMod, s.name)
|
||||
while e != nil:
|
||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
||||
if s.kind in ExportableSymKinds:
|
||||
rawImportSymbol(c, e, fromMod, importSet)
|
||||
e = nextIdentIter(it, fromMod.tab)
|
||||
e = nextModuleIter(it, c.graph)
|
||||
else:
|
||||
rawImportSymbol(c, s, fromMod, importSet)
|
||||
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
|
||||
proc addImport(c: PContext; im: sink ImportedModule) =
|
||||
for i in 0..high(c.imports):
|
||||
@@ -170,23 +171,11 @@ template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
|
||||
addPattern(c, it)
|
||||
for it in c.graph.ifaces[fromMod.position].pureEnums:
|
||||
if filter:
|
||||
importPureEnumFields(c, it, it.typ)
|
||||
importPureEnumFields(c, it.sym, it.sym.typ)
|
||||
|
||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
|
||||
addUnnamedIt(c, fromMod, it.id notin exceptSet)
|
||||
|
||||
when false:
|
||||
var i: TTabIter
|
||||
var s = initTabIter(i, fromMod.tab)
|
||||
while s != nil:
|
||||
if s.kind != skModule:
|
||||
if s.kind != skEnumField:
|
||||
if s.kind notin ExportableSymKinds:
|
||||
internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s)
|
||||
if exceptSet.isNil or s.name.id notin exceptSet:
|
||||
rawImportSymbol(c, s, fromMod)
|
||||
s = nextIter(i, fromMod.tab)
|
||||
addUnnamedIt(c, fromMod, it.sym.id notin exceptSet)
|
||||
|
||||
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
||||
c.addImport ImportedModule(m: fromMod, mode: importAll)
|
||||
@@ -220,12 +209,13 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
|
||||
localError(c.config, n.info, "module alias must be an identifier")
|
||||
elif n[1].ident.id != realModule.name.id:
|
||||
# some misguided guy will write 'import abc.foo as foo' ...
|
||||
result = createModuleAlias(realModule, nextId c.idgen, n[1].ident, realModule.info,
|
||||
result = createModuleAlias(realModule, nextSymId c.idgen, n[1].ident, realModule.info,
|
||||
c.config.options)
|
||||
|
||||
proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
|
||||
let f = checkModuleName(c.config, n)
|
||||
if f != InvalidFileIdx:
|
||||
addImportFileDep(c, f)
|
||||
let L = c.graph.importStack.len
|
||||
let recursion = c.graph.importStack.find(f)
|
||||
c.graph.importStack.add f
|
||||
@@ -255,7 +245,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
|
||||
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
|
||||
else:
|
||||
message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
|
||||
suggestSym(c.config, n.info, result, c.graph.usageSym, false)
|
||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
||||
importStmtResult.add newSymNode(result, n.info)
|
||||
#newStrNode(toFullPath(c.config, f), n.info)
|
||||
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2018 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Basic type definitions the module graph needs in order to support
|
||||
## incremental compilations.
|
||||
|
||||
const nimIncremental* = defined(nimIncremental)
|
||||
|
||||
import options, lineinfos
|
||||
|
||||
when nimIncremental:
|
||||
import ast, msgs, intsets, btrees, db_sqlite, std / sha1, pathutils
|
||||
from strutils import parseInt
|
||||
from os import isAbsolute
|
||||
|
||||
type
|
||||
Writer* = object
|
||||
sstack*: seq[PSym] # a stack of symbols to process
|
||||
tstack*: seq[PType] # a stack of types to process
|
||||
tmarks*, smarks*: IntSet
|
||||
forwardedSyms*: seq[PSym]
|
||||
|
||||
Reader* = object
|
||||
syms*: BTree[int, PSym]
|
||||
types*: BTree[int, PType]
|
||||
|
||||
IncrementalCtx* = object
|
||||
db*: DbConn
|
||||
w*: Writer
|
||||
r*: Reader
|
||||
configChanged*: bool
|
||||
|
||||
proc init*(incr: var IncrementalCtx) =
|
||||
incr.w.sstack = @[]
|
||||
incr.w.tstack = @[]
|
||||
incr.w.tmarks = initIntSet()
|
||||
incr.w.smarks = initIntSet()
|
||||
incr.w.forwardedSyms = @[]
|
||||
incr.r.syms = initBTree[int, PSym]()
|
||||
incr.r.types = initBTree[int, PType]()
|
||||
|
||||
|
||||
proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: AbsoluteFile): string =
|
||||
result = msgs.getHash(conf, fileIdx)
|
||||
if result.len == 0 and isAbsolute(string fullpath):
|
||||
result = $secureHashFile(string fullpath)
|
||||
msgs.setHash(conf, fileIdx, result)
|
||||
|
||||
proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int =
|
||||
if fileIdx == FileIndex(-1): return -1
|
||||
let fullpath = toFullPath(conf, fileIdx)
|
||||
let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
|
||||
fullpath)
|
||||
let id = row[0]
|
||||
let fullhash = hashFileCached(conf, fileIdx, AbsoluteFile fullpath)
|
||||
if id.len == 0:
|
||||
result = int incr.db.insertID(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)",
|
||||
int(fileIdx), fullpath, fullhash)
|
||||
else:
|
||||
if row[1] != fullhash:
|
||||
incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
|
||||
result = parseInt(id)
|
||||
|
||||
proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex =
|
||||
if dbId == -1: return FileIndex(-1)
|
||||
let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId)
|
||||
doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
|
||||
result = fileInfoIdx(conf, AbsoluteFile fullpath)
|
||||
|
||||
|
||||
proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
|
||||
module, fileIdx: FileIndex;
|
||||
isIncludeFile: bool) =
|
||||
if conf.symbolFiles != v2Sf: return
|
||||
|
||||
let a = toDbFileId(incr, conf, module)
|
||||
let b = toDbFileId(incr, conf, fileIdx)
|
||||
|
||||
incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
|
||||
a, b, ord(isIncludeFile))
|
||||
|
||||
# --------------- Database model ---------------------------------------------
|
||||
|
||||
proc createDb*(db: DbConn) =
|
||||
db.exec(sql"""
|
||||
create table if not exists controlblock(
|
||||
idgen integer not null
|
||||
);
|
||||
""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists config(
|
||||
config varchar(8000) not null
|
||||
);
|
||||
""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists filenames(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
fullpath varchar(8000) not null,
|
||||
fullHash varchar(256) not null
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists modules(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
fullpath varchar(8000) not null,
|
||||
interfHash varchar(256) not null,
|
||||
fullHash varchar(256) not null,
|
||||
|
||||
created timestamp not null default (DATETIME('now'))
|
||||
);""")
|
||||
db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists deps(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
dependency integer not null,
|
||||
isIncludeFile integer not null,
|
||||
foreign key (module) references filenames(id),
|
||||
foreign key (dependency) references filenames(id)
|
||||
);""")
|
||||
db.exec(sql"""create index if not exists DepsIx on deps(module);""")
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists types(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
module integer not null,
|
||||
data blob not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index TypeByModuleIdx on types(module);"
|
||||
db.exec sql"create index TypeByNimIdIdx on types(nimid);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists syms(
|
||||
id integer primary key,
|
||||
nimid integer not null,
|
||||
module integer not null,
|
||||
name varchar(256) not null,
|
||||
data blob not null,
|
||||
exported int not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index if not exists SymNameIx on syms(name);"
|
||||
db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
|
||||
db.exec sql"create index SymByModuleIdx on syms(module);"
|
||||
db.exec sql"create index SymByNimIdIdx on syms(nimid);"
|
||||
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists toplevelstmts(
|
||||
id integer primary key,
|
||||
position integer not null,
|
||||
module integer not null,
|
||||
data blob not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
|
||||
db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists statics(
|
||||
id integer primary key,
|
||||
module integer not null,
|
||||
data blob not null,
|
||||
foreign key (module) references module(id)
|
||||
);
|
||||
""")
|
||||
db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
|
||||
db.exec sql"insert into controlblock(idgen) values (0)"
|
||||
|
||||
|
||||
else:
|
||||
type
|
||||
IncrementalCtx* = object
|
||||
|
||||
template init*(incr: IncrementalCtx) = discard
|
||||
|
||||
template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
|
||||
module, fileIdx: FileIndex;
|
||||
isIncludeFile: bool) =
|
||||
discard
|
||||
@@ -22,7 +22,16 @@ import
|
||||
from trees import exprStructuralEquivalent, getRoot
|
||||
|
||||
type
|
||||
Scope = object # well we do scope-based memory management. \
|
||||
Con = object
|
||||
owner: PSym
|
||||
g: ControlFlowGraph
|
||||
graph: ModuleGraph
|
||||
inLoop, inSpawn, inLoopCond: int
|
||||
uninit: IntSet # set of uninit'ed vars
|
||||
uninitComputed: bool
|
||||
idgen: IdGenerator
|
||||
|
||||
Scope = object # we do scope-based memory management.
|
||||
# a scope is comparable to an nkStmtListExpr like
|
||||
# (try: statements; dest = y(); finally: destructors(); dest)
|
||||
vars: seq[PSym]
|
||||
@@ -31,17 +40,6 @@ type
|
||||
needsTry: bool
|
||||
parent: ptr Scope
|
||||
|
||||
type
|
||||
Con = object
|
||||
owner: PSym
|
||||
g: ControlFlowGraph
|
||||
graph: ModuleGraph
|
||||
otherRead: PNode
|
||||
inLoop, inSpawn, inLoopCond: int
|
||||
uninit: IntSet # set of uninit'ed vars
|
||||
uninitComputed: bool
|
||||
idgen: IdGenerator
|
||||
|
||||
ProcessMode = enum
|
||||
normal
|
||||
consumed
|
||||
@@ -62,7 +60,7 @@ template dbg(body) =
|
||||
body
|
||||
|
||||
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
||||
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextId c.idgen, c.owner, info)
|
||||
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info)
|
||||
sym.typ = typ
|
||||
s.vars.add(sym)
|
||||
result = newSymNode(sym)
|
||||
@@ -73,123 +71,118 @@ proc nestedScope(parent: var Scope): Scope =
|
||||
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
|
||||
|
||||
proc isLastRead(location: PNode; cfg: ControlFlowGraph; otherRead: var PNode; pc, until: int): int =
|
||||
var pc = pc
|
||||
while pc < cfg.len and pc < until:
|
||||
case cfg[pc].kind
|
||||
of def:
|
||||
if instrTargets(cfg[pc].n, location) == Full:
|
||||
# the path leads to a redefinition of 's' --> abandon it.
|
||||
return high(int)
|
||||
elif instrTargets(cfg[pc].n, location) == Partial:
|
||||
# only partially writes to 's' --> can't sink 's', so this def reads 's'
|
||||
otherRead = cfg[pc].n
|
||||
return -1
|
||||
inc pc
|
||||
of use:
|
||||
if instrTargets(cfg[pc].n, location) != None:
|
||||
otherRead = cfg[pc].n
|
||||
return -1
|
||||
inc pc
|
||||
of goto:
|
||||
pc += cfg[pc].dest
|
||||
of fork:
|
||||
# every branch must lead to the last read of the location:
|
||||
var variantA = pc + 1
|
||||
var variantB = pc + cfg[pc].dest
|
||||
while variantA != variantB:
|
||||
if min(variantA, variantB) < 0: return -1
|
||||
if max(variantA, variantB) >= cfg.len or min(variantA, variantB) >= until:
|
||||
break
|
||||
if variantA < variantB:
|
||||
variantA = isLastRead(location, cfg, otherRead, variantA, min(variantB, until))
|
||||
else:
|
||||
variantB = isLastRead(location, cfg, otherRead, variantB, min(variantA, until))
|
||||
pc = min(variantA, variantB)
|
||||
return pc
|
||||
import sets, hashes, tables
|
||||
|
||||
proc isCursor(n: PNode; c: Con): bool =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
sfCursor in n.sym.flags
|
||||
of nkDotExpr:
|
||||
isCursor(n[1], c)
|
||||
of nkCheckedFieldExpr:
|
||||
isCursor(n[0], c)
|
||||
else:
|
||||
false
|
||||
proc hash(n: PNode): Hash = hash(cast[pointer](n))
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con): bool =
|
||||
# first we need to search for the instruction that belongs to 'n':
|
||||
var instr = -1
|
||||
let m = dfa.skipConvDfa(n)
|
||||
if m.kind == nkSym and sfSingleUsedTemp in m.sym.flags: return true
|
||||
type AliasCache = Table[(PNode, PNode), AliasKind]
|
||||
proc aliasesCached(cache: var AliasCache, obj, field: PNode): AliasKind =
|
||||
let key = (obj, field)
|
||||
if not cache.hasKey(key):
|
||||
cache[key] = aliases(obj, field)
|
||||
cache[key]
|
||||
|
||||
for i in 0..<c.g.len:
|
||||
# This comparison is correct and MUST not be ``instrTargets``:
|
||||
if c.g[i].kind == use and c.g[i].n == m:
|
||||
if instr < 0:
|
||||
instr = i
|
||||
break
|
||||
|
||||
dbg: echo "starting point for ", n, " is ", instr, " ", n.kind
|
||||
|
||||
if instr < 0: return false
|
||||
# we go through all paths beginning from 'instr+1' and need to
|
||||
# ensure that we don't find another 'use X' instruction.
|
||||
if instr+1 >= c.g.len: return true
|
||||
|
||||
c.otherRead = nil
|
||||
result = isLastRead(n, c.g, c.otherRead, instr+1, int.high) >= 0
|
||||
dbg: echo "ugh ", c.otherRead.isNil, " ", result
|
||||
|
||||
proc isFirstWrite(location: PNode; cfg: ControlFlowGraph; pc, until: int): int =
|
||||
var pc = pc
|
||||
proc collectLastReads(cfg: ControlFlowGraph; cache: var AliasCache, lastReads, potLastReads: var IntSet; pc: var int, until: int) =
|
||||
template aliasesCached(obj, field: PNode): untyped =
|
||||
aliasesCached(cache, obj, field)
|
||||
while pc < until:
|
||||
case cfg[pc].kind
|
||||
of def:
|
||||
if instrTargets(cfg[pc].n, location) != None:
|
||||
# a definition of 's' before ours makes ours not the first write
|
||||
return -1
|
||||
let potLastReadsCopy = potLastReads
|
||||
for r in potLastReadsCopy:
|
||||
if cfg[pc].n.aliasesCached(cfg[r].n) == yes:
|
||||
# the path leads to a redefinition of 's' --> sink 's'.
|
||||
lastReads.incl r
|
||||
potLastReads.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
|
||||
potLastReads.excl r
|
||||
|
||||
inc pc
|
||||
of use:
|
||||
if instrTargets(cfg[pc].n, location) != None:
|
||||
return -1
|
||||
let potLastReadsCopy = potLastReads
|
||||
for r in potLastReadsCopy:
|
||||
if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no:
|
||||
cfg[r].n.comment = '\n' & $pc
|
||||
potLastReads.excl r
|
||||
|
||||
potLastReads.incl pc
|
||||
|
||||
inc pc
|
||||
of goto:
|
||||
pc += cfg[pc].dest
|
||||
of fork:
|
||||
# every branch must not contain a def/use of our location:
|
||||
var variantA = pc + 1
|
||||
var variantB = pc + cfg[pc].dest
|
||||
while variantA != variantB:
|
||||
if min(variantA, variantB) < 0: return -1
|
||||
if max(variantA, variantB) > until:
|
||||
break
|
||||
var potLastReadsA, potLastReadsB = potLastReads
|
||||
var lastReadsA, lastReadsB: IntSet
|
||||
while variantA != variantB and max(variantA, variantB) < cfg.len and min(variantA, variantB) < until:
|
||||
if variantA < variantB:
|
||||
variantA = isFirstWrite(location, cfg, variantA, min(variantB, until))
|
||||
collectLastReads(cfg, cache, lastReadsA, potLastReadsA, variantA, min(variantB, until))
|
||||
else:
|
||||
variantB = isFirstWrite(location, cfg, variantB, min(variantA, until))
|
||||
collectLastReads(cfg, cache, lastReadsB, potLastReadsB, variantB, min(variantA, until))
|
||||
|
||||
# Add those last reads that were turned into last reads on both branches
|
||||
lastReads.incl lastReadsA * lastReadsB
|
||||
# Add those last reads that were turned into last reads on only one branch,
|
||||
# but where the read operation itself also belongs to only that branch
|
||||
lastReads.incl (lastReadsA + lastReadsB) - potLastReads
|
||||
|
||||
let oldPotLastReads = potLastReads
|
||||
potLastReads = initIntSet()
|
||||
|
||||
potLastReads.incl potLastReadsA + potLastReadsB
|
||||
|
||||
# Remove potential last reads that were invalidated in a branch,
|
||||
# but don't remove those which were turned into last reads on that branch
|
||||
potLastReads.excl ((oldPotLastReads - potLastReadsA) - lastReadsA)
|
||||
potLastReads.excl ((oldPotLastReads - potLastReadsB) - lastReadsB)
|
||||
|
||||
pc = min(variantA, variantB)
|
||||
return pc
|
||||
|
||||
proc collectFirstWrites(cfg: ControlFlowGraph; alreadySeen: var HashSet[PNode]; pc: var int, until: int) =
|
||||
while pc < until:
|
||||
case cfg[pc].kind
|
||||
of def:
|
||||
var alreadySeenThisNode = false
|
||||
for s in alreadySeen:
|
||||
if cfg[pc].n.aliases(s) != no or s.aliases(cfg[pc].n) != no:
|
||||
alreadySeenThisNode = true; break
|
||||
if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite
|
||||
else: cfg[pc].n.flags.incl nfFirstWrite
|
||||
|
||||
alreadySeen.incl cfg[pc].n
|
||||
|
||||
inc pc
|
||||
of use:
|
||||
alreadySeen.incl cfg[pc].n
|
||||
|
||||
inc pc
|
||||
of goto:
|
||||
pc += cfg[pc].dest
|
||||
of fork:
|
||||
var variantA = pc + 1
|
||||
var variantB = pc + cfg[pc].dest
|
||||
var alreadySeenA, alreadySeenB = alreadySeen
|
||||
while variantA != variantB and max(variantA, variantB) < cfg.len and min(variantA, variantB) < until:
|
||||
if variantA < variantB:
|
||||
collectFirstWrites(cfg, alreadySeenA, variantA, min(variantB, until))
|
||||
else:
|
||||
collectFirstWrites(cfg, alreadySeenB, variantB, min(variantA, until))
|
||||
|
||||
alreadySeen.incl alreadySeenA + alreadySeenB
|
||||
|
||||
pc = min(variantA, variantB)
|
||||
|
||||
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 =
|
||||
# first we need to search for the instruction that belongs to 'n':
|
||||
var instr = -1
|
||||
let m = dfa.skipConvDfa(n)
|
||||
|
||||
for i in countdown(c.g.len-1, 0): # We search backwards here to treat loops correctly
|
||||
if c.g[i].kind == def and c.g[i].n == m:
|
||||
if instr < 0:
|
||||
instr = i
|
||||
break
|
||||
|
||||
if instr < 0: return false
|
||||
# we go through all paths going to 'instr' and need to
|
||||
# ensure that we don't find another 'def/use X' instruction.
|
||||
if instr == 0: return true
|
||||
|
||||
result = isFirstWrite(n, c.g, 0, instr) >= 0
|
||||
nfFirstWrite in m.flags
|
||||
|
||||
proc initialized(code: ControlFlowGraph; pc: int,
|
||||
init, uninit: var IntSet; until: int): int =
|
||||
@@ -229,20 +222,33 @@ proc initialized(code: ControlFlowGraph; pc: int,
|
||||
inc pc
|
||||
return pc
|
||||
|
||||
proc isCursor(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
sfCursor in n.sym.flags
|
||||
of nkDotExpr:
|
||||
isCursor(n[1])
|
||||
of nkCheckedFieldExpr:
|
||||
isCursor(n[0])
|
||||
else:
|
||||
false
|
||||
|
||||
template isUnpackedTuple(n: PNode): bool =
|
||||
## we move out all elements of unpacked tuples,
|
||||
## hence unpacked tuples themselves don't need to be destroyed
|
||||
(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) =
|
||||
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
||||
if (opname == "=" or opname == "=copy") and ri != nil:
|
||||
m.add "; requires a copy because it's not the last read of '"
|
||||
m.add renderTree(ri)
|
||||
m.add '\''
|
||||
if c.otherRead != nil:
|
||||
if ri.comment.startsWith('\n'):
|
||||
m.add "; another read is done here: "
|
||||
m.add c.graph.config $ c.otherRead.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):
|
||||
m.add "; try to make "
|
||||
m.add renderTree(ri)
|
||||
@@ -252,7 +258,7 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
||||
localError(c.graph.config, ri.info, errGenerated, m)
|
||||
|
||||
proc makePtrType(c: var Con, baseType: PType): PType =
|
||||
result = newType(tyPtr, nextId c.idgen, c.owner)
|
||||
result = newType(tyPtr, nextTypeId c.idgen, c.owner)
|
||||
addSonSkipIntLit(result, baseType, c.idgen)
|
||||
|
||||
proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
|
||||
@@ -261,18 +267,18 @@ proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
|
||||
result = newTree(nkCall, newSymNode(op), addrExp)
|
||||
|
||||
proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
|
||||
var op = t.attachedOps[kind]
|
||||
if op == nil or op.ast[genericParamsPos].kind != nkEmpty:
|
||||
var op = getAttachedOp(c.graph, t, kind)
|
||||
if op == nil or op.ast.isGenericRoutine:
|
||||
# give up and find the canonical type instead:
|
||||
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
|
||||
let canon = c.graph.canonTypes.getOrDefault(h)
|
||||
if canon != nil:
|
||||
op = canon.attachedOps[kind]
|
||||
op = getAttachedOp(c.graph, canon, kind)
|
||||
if op == nil:
|
||||
#echo dest.typ.id
|
||||
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
|
||||
"' operator not found for type " & typeToString(t))
|
||||
elif op.ast[genericParamsPos].kind != nkEmpty:
|
||||
elif op.ast.isGenericRoutine:
|
||||
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
|
||||
"' operator is generic")
|
||||
dbg:
|
||||
@@ -288,9 +294,9 @@ proc genDestroy(c: var Con; dest: PNode): PNode =
|
||||
proc canBeMoved(c: Con; t: PType): bool {.inline.} =
|
||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if optOwnedRefs in c.graph.config.globalOptions:
|
||||
result = t.kind != tyRef and t.attachedOps[attachedSink] != nil
|
||||
result = t.kind != tyRef and getAttachedOp(c.graph, t, attachedSink) != nil
|
||||
else:
|
||||
result = t.attachedOps[attachedSink] != nil
|
||||
result = getAttachedOp(c.graph, t, attachedSink) != nil
|
||||
|
||||
proc isNoInit(dest: PNode): bool {.inline.} =
|
||||
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
|
||||
@@ -303,7 +309,7 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
|
||||
result = newTree(nkFastAsgn, dest, ri)
|
||||
else:
|
||||
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if t.attachedOps[attachedSink] != nil:
|
||||
if getAttachedOp(c.graph, t, attachedSink) != nil:
|
||||
result = c.genOp(t, attachedSink, dest, ri)
|
||||
result.add ri
|
||||
else:
|
||||
@@ -344,7 +350,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
|
||||
if t.kind == tyRef:
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
|
||||
else:
|
||||
let xenv = genBuiltin(c.graph, mAccessEnv, "accessEnv", dest)
|
||||
let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
|
||||
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
||||
|
||||
@@ -356,7 +362,6 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode =
|
||||
let t = dest.typ
|
||||
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||
# try to improve the error message here:
|
||||
if c.otherRead == nil: discard isLastRead(ri, c)
|
||||
c.checkForErrorPragma(t, ri, "=copy")
|
||||
result = c.genCopyNoCheck(dest, ri)
|
||||
|
||||
@@ -376,8 +381,8 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
||||
let objType = leDotExpr[0].typ
|
||||
|
||||
if hasDestructor(c, objType):
|
||||
if objType.attachedOps[attachedDestructor] != nil and
|
||||
sfOverriden in objType.attachedOps[attachedDestructor].flags:
|
||||
if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
|
||||
sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
|
||||
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
|
||||
It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
|
||||
result.add newTree(nkFastAsgn, le, tmp)
|
||||
@@ -390,21 +395,21 @@ It is best to factor out piece of object that needs custom destructor into separ
|
||||
cond.add le
|
||||
cond.add tmp
|
||||
let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
|
||||
notExpr.add newSymNode(createMagic(c.graph, "not", mNot))
|
||||
notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
|
||||
notExpr.add cond
|
||||
result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
|
||||
result.add newTree(nkFastAsgn, le, tmp)
|
||||
|
||||
proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved)))
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
|
||||
result.add copyTree(n) #mWasMoved does not take the address
|
||||
#if n.kind != nkSym:
|
||||
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
|
||||
|
||||
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
result = newNodeI(nkCall, info)
|
||||
result.add(newSymNode(createMagic(c.graph, "default", mDefault)))
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
|
||||
result.typ = t
|
||||
|
||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
@@ -415,7 +420,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextId c.idgen, c.owner, n.info)
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info)
|
||||
temp.typ = n.typ
|
||||
var v = newNodeI(nkLetSection, n.info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
@@ -523,23 +528,19 @@ proc cycleCheck(n: PNode; c: var Con) =
|
||||
message(c.graph.config, n.info, warnCycleCreated, msg)
|
||||
break
|
||||
|
||||
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) =
|
||||
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
|
||||
# move the variable declaration to the top of the frame:
|
||||
s.vars.add v.sym
|
||||
if isUnpackedTuple(v):
|
||||
if c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
elif sfThread 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.
|
||||
if sfGlobal in v.sym.flags and s.parent == nil: #XXX: Rethink this logic (see tarcmisc.test2)
|
||||
c.graph.globalDestructors.add c.genDestroy(v)
|
||||
else:
|
||||
s.final.add c.genDestroy(v)
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true)
|
||||
elif ri.kind != nkEmpty:
|
||||
res.add moveOrCopy(v, ri, c, s, isDecl = true)
|
||||
|
||||
proc processScope(c: var Con; s: var Scope; ret: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, ret.info)
|
||||
@@ -739,7 +740,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||
nkCallKinds + nkLiterals:
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c) and not (n.kind == nkSym and isCursor(n, c)):
|
||||
isLastRead(n, c) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
result = destructiveMoveVar(n, c, s)
|
||||
@@ -845,17 +846,16 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||
let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
|
||||
result.add p(x, c, s, consumed)
|
||||
elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ) and not isCursor(it[0], c):
|
||||
elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ):
|
||||
for j in 0..<it.len-2:
|
||||
let v = it[j]
|
||||
if v.kind == nkSym:
|
||||
if sfCompileTime in v.sym.flags: continue
|
||||
pVarTopLevel(v, c, s, ri, result)
|
||||
else:
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
ri = genDefaultCall(v.typ, c, v.info)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c, s, isDecl = false)
|
||||
pVarTopLevel(v, c, s, result)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c, s, isDecl = v.kind == nkSym)
|
||||
elif ri.kind == nkEmpty and c.inLoop > 0:
|
||||
result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = v.kind == nkSym)
|
||||
else: # keep the var but transform 'ri':
|
||||
var v = copyNode(n)
|
||||
var itCopy = copyNode(it)
|
||||
@@ -865,8 +865,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||
v.add itCopy
|
||||
result.add v
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and
|
||||
not isCursor(n[0], c):
|
||||
if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
|
||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||
cycleCheck(n, c)
|
||||
assert n[1].kind notin {nkAsgn, nkFastAsgn}
|
||||
@@ -885,7 +884,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr:
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
result = n
|
||||
|
||||
of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock:
|
||||
@@ -968,68 +968,105 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
|
||||
else:
|
||||
internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind)
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode =
|
||||
case ri.kind
|
||||
of nkCallKinds:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkBracketExpr:
|
||||
if isUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||
not aliases(dest, ri):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
var snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkSym:
|
||||
if dest.kind == nkSym and dest.sym == ri.sym:
|
||||
# rule (self-assignment-removal):
|
||||
result = newNodeI(nkEmpty, dest.info)
|
||||
elif isSinkParam(ri.sym) and isLastRead(ri, c):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
||||
isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri, c):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv:
|
||||
result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
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
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
of nkRaiseStmt:
|
||||
result = pRaiseStmt(ri, c, s)
|
||||
proc sameLocation*(a, b: PNode): bool =
|
||||
proc sameConstant(a, b: PNode): bool =
|
||||
a.kind in nkLiterals and a.intVal == b.intVal
|
||||
|
||||
const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
|
||||
if a.kind in nkEndPoint and b.kind in nkEndPoint:
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym: a.sym == b.sym
|
||||
of nkDotExpr, nkCheckedFieldExpr: sameLocation(a[0], b[0]) and sameLocation(a[1], b[1])
|
||||
of nkBracketExpr: sameLocation(a[0], b[0]) and sameConstant(a[1], b[1])
|
||||
else: false
|
||||
else: false
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||
canBeMoved(c, dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
case a.kind
|
||||
of nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
# Reached an endpoint, flip to recurse the other side.
|
||||
sameLocation(b, a)
|
||||
of nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref:
|
||||
# We don't need to check addr/deref levels or differentiate between the two,
|
||||
# since pointers don't have hooks :) (e.g: var p: ptr pointer; p[] = addr p)
|
||||
sameLocation(a[0], b)
|
||||
of nkObjDownConv, nkObjUpConv: sameLocation(a[0], b)
|
||||
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
|
||||
else: false
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode =
|
||||
if sameLocation(dest, ri):
|
||||
# rule (self-assignment-removal):
|
||||
result = newNodeI(nkEmpty, dest.info)
|
||||
elif isCursor(dest):
|
||||
case ri.kind:
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
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
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
else:
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
result = newTree(nkFastAsgn, dest, p(ri, c, s, normal))
|
||||
else:
|
||||
case ri.kind
|
||||
of nkCallKinds:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkBracketExpr:
|
||||
if isUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
|
||||
if aliases(dest, ri) == no:
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
var snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
result = c.genSink(dest, destructiveMoveVar(ri, c, s), isDecl)
|
||||
else:
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
|
||||
of nkSym:
|
||||
if isSinkParam(ri.sym) and isLastRead(ri, c):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
||||
isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv:
|
||||
result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
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
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
of nkRaiseStmt:
|
||||
result = pRaiseStmt(ri, c, s)
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||
canBeMoved(c, dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, isDecl)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
result = c.genCopy(dest, ri)
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
|
||||
proc computeUninit(c: var Con) =
|
||||
if not c.uninitComputed:
|
||||
@@ -1067,7 +1104,28 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n:
|
||||
echo n
|
||||
|
||||
if optCursorInference in g.config.options:
|
||||
computeCursors(owner, n, g.config)
|
||||
computeCursors(owner, n, g)
|
||||
|
||||
block:
|
||||
var cache = initTable[(PNode, PNode), AliasKind]()
|
||||
var lastReads, potLastReads: IntSet
|
||||
var pc = 0
|
||||
collectLastReads(c.g, cache, lastReads, potLastReads, pc, c.g.len)
|
||||
lastReads.incl potLastReads
|
||||
var lastReadTable: Table[PNode, seq[int]]
|
||||
for position, node in c.g:
|
||||
if node.kind == use:
|
||||
lastReadTable.mgetOrPut(node.n, @[]).add position
|
||||
for node, positions in lastReadTable:
|
||||
var allPositionsLastRead = true
|
||||
for p in positions:
|
||||
if p notin lastReads: allPositionsLastRead = false; break
|
||||
if allPositionsLastRead:
|
||||
node.flags.incl nfLastRead
|
||||
|
||||
var alreadySeen: HashSet[PNode]
|
||||
pc = 0
|
||||
collectFirstWrites(c.g, alreadySeen, pc, c.g.len)
|
||||
|
||||
var scope: Scope
|
||||
let body = p(n, c, scope, normal)
|
||||
|
||||
@@ -76,7 +76,6 @@ Files: "lib"
|
||||
[Other]
|
||||
Files: "examples"
|
||||
Files: "dist/nimble"
|
||||
Files: "dist/fusion"
|
||||
|
||||
Files: "tests"
|
||||
|
||||
@@ -110,6 +109,7 @@ Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/downlo
|
||||
Files: "bin/makelink.exe"
|
||||
Files: "bin/7zG.exe"
|
||||
Files: "bin/*.dll"
|
||||
Files: "bin/cacert.pem"
|
||||
|
||||
[UnixBin]
|
||||
Files: "bin/nim"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
## This module is for compiler internal use only. For reliable error
|
||||
## messages and range checks, the compiler needs a data type that can
|
||||
## hold all from ``low(BiggestInt)`` to ``high(BiggestUInt)``, This
|
||||
## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
|
||||
## type is for that purpose.
|
||||
|
||||
from math import trunc
|
||||
from std/math import trunc
|
||||
|
||||
type
|
||||
Int128* = object
|
||||
udata: array[4,uint32]
|
||||
udata: array[4, uint32]
|
||||
|
||||
template sdata(arg: Int128, idx: int): int32 =
|
||||
# udata and sdata was supposed to be in a union, but unions are
|
||||
@@ -17,12 +17,12 @@ template sdata(arg: Int128, idx: int): int32 =
|
||||
# encoding least significant int first (like LittleEndian)
|
||||
|
||||
const
|
||||
Zero* = Int128(udata: [0'u32,0,0,0])
|
||||
One* = Int128(udata: [1'u32,0,0,0])
|
||||
Ten* = Int128(udata: [10'u32,0,0,0])
|
||||
Min = Int128(udata: [0'u32,0,0,0x80000000'u32])
|
||||
Max = Int128(udata: [high(uint32),high(uint32),high(uint32),uint32(high(int32))])
|
||||
NegOne* = Int128(udata: [0xffffffff'u32,0xffffffff'u32,0xffffffff'u32,0xffffffff'u32])
|
||||
Zero* = Int128(udata: [0'u32, 0, 0, 0])
|
||||
One* = Int128(udata: [1'u32, 0, 0, 0])
|
||||
Ten* = Int128(udata: [10'u32, 0, 0, 0])
|
||||
Min = Int128(udata: [0'u32, 0, 0, 0x80000000'u32])
|
||||
Max = Int128(udata: [high(uint32), high(uint32), high(uint32), uint32(high(int32))])
|
||||
NegOne* = Int128(udata: [0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32])
|
||||
|
||||
template low*(t: typedesc[Int128]): Int128 = Min
|
||||
template high*(t: typedesc[Int128]): Int128 = Max
|
||||
@@ -57,10 +57,10 @@ template isNegative(arg: Int128): bool =
|
||||
template isNegative(arg: int32): bool =
|
||||
arg < 0
|
||||
|
||||
proc bitconcat(a,b: uint32): uint64 =
|
||||
proc bitconcat(a, b: uint32): uint64 =
|
||||
(uint64(a) shl 32) or uint64(b)
|
||||
|
||||
proc bitsplit(a: uint64): (uint32,uint32) =
|
||||
proc bitsplit(a: uint64): (uint32, uint32) =
|
||||
(cast[uint32](a shr 32), cast[uint32](a))
|
||||
|
||||
proc toInt64*(arg: Int128): int64 =
|
||||
@@ -176,7 +176,6 @@ proc toHex*(arg: Int128): string =
|
||||
result.addToHex(arg)
|
||||
|
||||
proc inc*(a: var Int128, y: uint32 = 1) =
|
||||
let input = a
|
||||
a.udata[0] += y
|
||||
if unlikely(a.udata[0] < y):
|
||||
a.udata[1].inc
|
||||
@@ -186,7 +185,7 @@ proc inc*(a: var Int128, y: uint32 = 1) =
|
||||
a.udata[3].inc
|
||||
doAssert(a.sdata(3) != low(int32), "overflow")
|
||||
|
||||
proc cmp*(a,b: Int128): int =
|
||||
proc cmp*(a, b: Int128): int =
|
||||
let tmp1 = cmp(a.sdata(3), b.sdata(3))
|
||||
if tmp1 != 0: return tmp1
|
||||
let tmp2 = cmp(a.udata[2], b.udata[2])
|
||||
@@ -196,13 +195,13 @@ proc cmp*(a,b: Int128): int =
|
||||
let tmp4 = cmp(a.udata[0], b.udata[0])
|
||||
return tmp4
|
||||
|
||||
proc `<`*(a,b: Int128): bool =
|
||||
cmp(a,b) < 0
|
||||
proc `<`*(a, b: Int128): bool =
|
||||
cmp(a, b) < 0
|
||||
|
||||
proc `<=`*(a,b: Int128): bool =
|
||||
cmp(a,b) <= 0
|
||||
proc `<=`*(a, b: Int128): bool =
|
||||
cmp(a, b) <= 0
|
||||
|
||||
proc `==`*(a,b: Int128): bool =
|
||||
proc `==`*(a, b: Int128): bool =
|
||||
if a.udata[0] != b.udata[0]: return false
|
||||
if a.udata[1] != b.udata[1]: return false
|
||||
if a.udata[2] != b.udata[2]: return false
|
||||
@@ -221,19 +220,19 @@ proc bitnot*(a: Int128): Int128 =
|
||||
result.udata[2] = not a.udata[2]
|
||||
result.udata[3] = not a.udata[3]
|
||||
|
||||
proc bitand*(a,b: Int128): Int128 =
|
||||
proc bitand*(a, b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] and b.udata[0]
|
||||
result.udata[1] = a.udata[1] and b.udata[1]
|
||||
result.udata[2] = a.udata[2] and b.udata[2]
|
||||
result.udata[3] = a.udata[3] and b.udata[3]
|
||||
|
||||
proc bitor*(a,b: Int128): Int128 =
|
||||
proc bitor*(a, b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] or b.udata[0]
|
||||
result.udata[1] = a.udata[1] or b.udata[1]
|
||||
result.udata[2] = a.udata[2] or b.udata[2]
|
||||
result.udata[3] = a.udata[3] or b.udata[3]
|
||||
|
||||
proc bitxor*(a,b: Int128): Int128 =
|
||||
proc bitxor*(a, b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] xor b.udata[0]
|
||||
result.udata[1] = a.udata[1] xor b.udata[1]
|
||||
result.udata[2] = a.udata[2] xor b.udata[2]
|
||||
@@ -288,7 +287,7 @@ proc `shl`*(a: Int128, b: int): Int128 =
|
||||
result.udata[2] = 0
|
||||
result.udata[3] = a.udata[0] shl (b and 31)
|
||||
|
||||
proc `+`*(a,b: Int128): Int128 =
|
||||
proc `+`*(a, b: Int128): Int128 =
|
||||
let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
|
||||
result.udata[0] = cast[uint32](tmp0)
|
||||
let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
|
||||
@@ -305,7 +304,7 @@ proc `-`*(a: Int128): Int128 =
|
||||
result = bitnot(a)
|
||||
result.inc
|
||||
|
||||
proc `-`*(a,b: Int128): Int128 =
|
||||
proc `-`*(a, b: Int128): Int128 =
|
||||
a + (-b)
|
||||
|
||||
proc `-=`*(a: var Int128, b: Int128) =
|
||||
@@ -342,7 +341,7 @@ proc `*`*(a: Int128, b: int32): Int128 =
|
||||
proc `*=`*(a: var Int128, b: int32): Int128 =
|
||||
result = result * b
|
||||
|
||||
proc makeInt128(high,low: uint64): Int128 =
|
||||
proc makeInt128(high, low: uint64): Int128 =
|
||||
result.udata[0] = cast[uint32](low)
|
||||
result.udata[1] = cast[uint32](low shr 32)
|
||||
result.udata[2] = cast[uint32](high)
|
||||
@@ -354,7 +353,7 @@ proc high64(a: Int128): uint64 =
|
||||
proc low64(a: Int128): uint64 =
|
||||
bitconcat(a.udata[1], a.udata[0])
|
||||
|
||||
proc `*`*(lhs,rhs: Int128): Int128 =
|
||||
proc `*`*(lhs, rhs: Int128): Int128 =
|
||||
let
|
||||
a = cast[uint64](lhs.udata[0])
|
||||
b = cast[uint64](lhs.udata[1])
|
||||
@@ -379,7 +378,7 @@ proc `*`*(lhs,rhs: Int128): Int128 =
|
||||
proc `*=`*(a: var Int128, b: Int128) =
|
||||
a = a * b
|
||||
|
||||
import bitops
|
||||
import std/bitops
|
||||
|
||||
proc fastLog2*(a: Int128): int =
|
||||
if a.udata[3] != 0:
|
||||
@@ -389,7 +388,7 @@ proc fastLog2*(a: Int128): int =
|
||||
if a.udata[1] != 0:
|
||||
return 32 + fastLog2(a.udata[1])
|
||||
if a.udata[0] != 0:
|
||||
return fastLog2(a.udata[0])
|
||||
return fastLog2(a.udata[0])
|
||||
|
||||
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
assert(divisor != Zero)
|
||||
@@ -441,12 +440,12 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
else:
|
||||
result.remainder = dividend
|
||||
|
||||
proc `div`*(a,b: Int128): Int128 =
|
||||
let (a,b) = divMod(a,b)
|
||||
proc `div`*(a, b: Int128): Int128 =
|
||||
let (a, b) = divMod(a, b)
|
||||
return a
|
||||
|
||||
proc `mod`*(a,b: Int128): Int128 =
|
||||
let (a,b) = divMod(a,b)
|
||||
proc `mod`*(a, b: Int128): Int128 =
|
||||
let (a, b) = divMod(a, b)
|
||||
return b
|
||||
|
||||
proc addInt128*(result: var string; value: Int128) =
|
||||
@@ -477,7 +476,7 @@ proc `$`*(a: Int128): string =
|
||||
|
||||
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
assert(pos < arg.len)
|
||||
assert(arg[pos] in {'-','0'..'9'})
|
||||
assert(arg[pos] in {'-', '0'..'9'})
|
||||
|
||||
var isNegative = false
|
||||
var pos = pos
|
||||
@@ -497,13 +496,13 @@ proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
# fluff
|
||||
|
||||
proc `<`*(a: Int128, b: BiggestInt): bool =
|
||||
cmp(a,toInt128(b)) < 0
|
||||
cmp(a, toInt128(b)) < 0
|
||||
|
||||
proc `<`*(a: BiggestInt, b: Int128): bool =
|
||||
cmp(toInt128(a), b) < 0
|
||||
|
||||
proc `<=`*(a: Int128, b: BiggestInt): bool =
|
||||
cmp(a,toInt128(b)) <= 0
|
||||
cmp(a, toInt128(b)) <= 0
|
||||
|
||||
proc `<=`*(a: BiggestInt, b: Int128): bool =
|
||||
cmp(toInt128(a), b) <= 0
|
||||
@@ -539,7 +538,7 @@ proc toFloat64*(arg: Int128): float64 =
|
||||
|
||||
proc ldexp(x: float64, exp: cint): float64 {.importc: "ldexp", header: "<math.h>".}
|
||||
|
||||
template bitor(a,b,c: Int128): Int128 = bitor(bitor(a,b), c)
|
||||
template bitor(a, b, c: Int128): Int128 = bitor(bitor(a, b), c)
|
||||
|
||||
proc toInt128*(arg: float64): Int128 =
|
||||
let isNegative = arg < 0
|
||||
|
||||
@@ -14,27 +14,28 @@ The JS code generator contains only 2 tricks:
|
||||
|
||||
Trick 1
|
||||
-------
|
||||
Some locations (for example 'var int') require "fat pointers" (``etyBaseIndex``)
|
||||
Some locations (for example 'var int') require "fat pointers" (`etyBaseIndex`)
|
||||
which are pairs (array, index). The derefence operation is then 'array[index]'.
|
||||
Check ``mapType`` for the details.
|
||||
Check `mapType` for the details.
|
||||
|
||||
Trick 2
|
||||
-------
|
||||
It is preferable to generate '||' and '&&' if possible since that is more
|
||||
idiomatic and hence should be friendlier for the JS JIT implementation. However
|
||||
code like ``foo and (let bar = baz())`` cannot be translated this way. Instead
|
||||
the expressions need to be transformed into statements. ``isSimpleExpr``
|
||||
code like `foo and (let bar = baz())` cannot be translated this way. Instead
|
||||
the expressions need to be transformed into statements. `isSimpleExpr`
|
||||
implements the required case distinction.
|
||||
"""
|
||||
|
||||
|
||||
import
|
||||
ast, strutils, trees, magicsys, options,
|
||||
nversion, msgs, idents, types, tables,
|
||||
ropes, math, passes, ccgutils, wordrecg, renderer,
|
||||
intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
|
||||
transf, injectdestructors, sourcemap, json, sets
|
||||
ast, trees, magicsys, options,
|
||||
nversion, msgs, idents, types,
|
||||
ropes, passes, ccgutils, wordrecg, renderer,
|
||||
cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
|
||||
transf, injectdestructors, sourcemap
|
||||
|
||||
import std/[json, sets, math, tables, intsets, strutils]
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
@@ -210,7 +211,7 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||
else: result = etyNone
|
||||
of tyProc: result = etyProc
|
||||
of tyCString: result = etyString
|
||||
of tyOptDeprecated: doAssert false
|
||||
of tyConcept: doAssert false
|
||||
|
||||
proc mapType(p: PProc; typ: PType): TJSTypeKind =
|
||||
result = mapType(typ)
|
||||
@@ -670,7 +671,10 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mAddU: binaryUintExpr(p, n, r, "+")
|
||||
of mSubU: binaryUintExpr(p, n, r, "-")
|
||||
of mMulU: binaryUintExpr(p, n, r, "*")
|
||||
of mDivU: binaryUintExpr(p, n, r, "/")
|
||||
of mDivU:
|
||||
binaryUintExpr(p, n, r, "/")
|
||||
if n[1].typ.skipTypes(abstractRange).size == 8:
|
||||
r.res = "Math.trunc($1)" % [r.res]
|
||||
of mDivI:
|
||||
arithAux(p, n, r, op)
|
||||
of mModI:
|
||||
@@ -887,7 +891,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var v = copyNode(e[0])
|
||||
inc(totalRange, int(e[1].intVal - v.intVal))
|
||||
if totalRange > 65535:
|
||||
localError(p.config, n.info,
|
||||
localError(p.config, n.info,
|
||||
"Your case statement contains too many branches, consider using if/else instead!")
|
||||
while v.intVal <= e[1].intVal:
|
||||
gen(p, v, cond)
|
||||
@@ -1076,11 +1080,11 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
useMagic(p, "nimCopy")
|
||||
# supports proc getF(): var T
|
||||
if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds:
|
||||
lineF(p, "nimCopy($1, $2, $3);$n",
|
||||
[a.res, b.res, genTypeInfo(p, y.typ)])
|
||||
lineF(p, "nimCopy($1, $2, $3);$n",
|
||||
[a.res, b.res, genTypeInfo(p, x.typ)])
|
||||
else:
|
||||
lineF(p, "$1 = nimCopy($1, $2, $3);$n",
|
||||
[a.res, b.res, genTypeInfo(p, y.typ)])
|
||||
[a.res, b.res, genTypeInfo(p, x.typ)])
|
||||
of etyBaseIndex:
|
||||
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
|
||||
if y.kind == nkCall:
|
||||
@@ -1350,7 +1354,15 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkStmtListExpr:
|
||||
if n.len == 1: gen(p, n[0], r)
|
||||
else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr")
|
||||
else: internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
|
||||
of nkCallKinds:
|
||||
if n[0].typ.kind == tyOpenArray:
|
||||
# 'var openArray' for instance produces an 'addr' but this is harmless:
|
||||
# namely toOpenArray(a, 1, 3)
|
||||
gen(p, n[0], r)
|
||||
else:
|
||||
internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
|
||||
else:
|
||||
internalError(p.config, n[0].info, "genAddr: " & $n[0].kind)
|
||||
|
||||
proc attachProc(p: PProc; content: Rope; s: PSym) =
|
||||
p.g.code.add(content)
|
||||
@@ -1423,10 +1435,10 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
s.name.s)
|
||||
discard mangleName(p.module, s)
|
||||
r.res = s.loc.r
|
||||
if lfNoDecl in s.loc.flags or s.magic != mNone or
|
||||
if lfNoDecl in s.loc.flags or s.magic notin {mNone, mIsolate} or
|
||||
{sfImportc, sfInfixCall} * s.flags != {}:
|
||||
discard
|
||||
elif s.kind == skMethod and s.getBody.kind == nkEmpty:
|
||||
elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty:
|
||||
# we cannot produce code for the dispatcher yet:
|
||||
discard
|
||||
elif sfForward in s.flags:
|
||||
@@ -1677,7 +1689,10 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
var t = skipTypes(typ, abstractInst)
|
||||
case t.kind
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
|
||||
result = putToSeq("0", indirect)
|
||||
if $t.sym.loc.r == "bigint":
|
||||
result = putToSeq("0n", indirect)
|
||||
else:
|
||||
result = putToSeq("0", indirect)
|
||||
of tyFloat..tyFloat128:
|
||||
result = putToSeq("0.0", indirect)
|
||||
of tyRange, tyGenericInst, tyAlias, tySink, tyOwned:
|
||||
@@ -1979,6 +1994,23 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) =
|
||||
genReset(p, n)
|
||||
#lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
|
||||
|
||||
proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
r.res = rope("[")
|
||||
r.kind = resExpr
|
||||
for i in 0 ..< n.len:
|
||||
if i > 0: r.res.add(", ")
|
||||
gen(p, n[i], a)
|
||||
if a.typ == etyBaseIndex:
|
||||
r.res.addf("[$1, $2]", [a.address, a.res])
|
||||
else:
|
||||
if not needsNoCopy(p, n[i]):
|
||||
let typ = n[i].typ.skipTypes(abstractInst)
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
r.res.add(a.res)
|
||||
r.res.add("]")
|
||||
|
||||
proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var
|
||||
a: TCompRes
|
||||
@@ -1999,7 +2031,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
gen(p, n[2], rhs)
|
||||
|
||||
if skipTypes(n[1].typ, abstractVarRange).kind == tyCString:
|
||||
r.res = "$1 += $2;" % [lhs.rdLoc, rhs.rdLoc]
|
||||
let (b, tmp) = maybeMakeTemp(p, n[2], rhs)
|
||||
r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" %
|
||||
[b, lhs.rdLoc, tmp]
|
||||
else:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
|
||||
r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
|
||||
@@ -2040,8 +2074,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mNew, mNewFinalize: genNew(p, n)
|
||||
of mChr: gen(p, n[1], r)
|
||||
of mArrToSeq:
|
||||
if needsNoCopy(p, n[1]):
|
||||
gen(p, n[1], r)
|
||||
# only array literals doesn't need copy
|
||||
if n[1].kind == nkBracket:
|
||||
genJSArrayConstr(p, n[1], r)
|
||||
else:
|
||||
var x: TCompRes
|
||||
gen(p, n[1], x)
|
||||
@@ -2050,9 +2085,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mDestroy: discard "ignore calls to the default destructor"
|
||||
of mOrd: genOrd(p, n, r)
|
||||
of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
|
||||
unaryExpr(p, n, r, "", "($1).length")
|
||||
var x: TCompRes
|
||||
gen(p, n[1], x)
|
||||
if skipTypes(n[1].typ, abstractInst).kind == tyCString:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
r.res = "(($1) == null ? 0 : ($2).length)" % [a, tmp]
|
||||
else:
|
||||
r.res = "($1).length" % [x.rdLoc]
|
||||
r.kind = resExpr
|
||||
of mHigh:
|
||||
unaryExpr(p, n, r, "", "(($1).length-1)")
|
||||
var x: TCompRes
|
||||
gen(p, n[1], x)
|
||||
if skipTypes(n[1].typ, abstractInst).kind == tyCString:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
r.res = "(($1) == null ? -1 : ($2).length - 1)" % [a, tmp]
|
||||
else:
|
||||
r.res = "($1).length - 1" % [x.rdLoc]
|
||||
r.kind = resExpr
|
||||
of mInc:
|
||||
if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
|
||||
binaryUintExpr(p, n, r, "+", true)
|
||||
@@ -2131,7 +2180,11 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
if it.kind == nkRange:
|
||||
gen(p, it[0], a)
|
||||
gen(p, it[1], b)
|
||||
r.res.addf("[$1, $2]", [a.res, b.res])
|
||||
|
||||
if it[0].typ.kind == tyBool:
|
||||
r.res.addf("$1, $2", [a.res, b.res])
|
||||
else:
|
||||
r.res.addf("[$1, $2]", [a.res, b.res])
|
||||
else:
|
||||
gen(p, it, a)
|
||||
r.res.add(a.res)
|
||||
@@ -2144,21 +2197,26 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res = tmp
|
||||
|
||||
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
r.res = rope("[")
|
||||
r.kind = resExpr
|
||||
for i in 0..<n.len:
|
||||
if i > 0: r.res.add(", ")
|
||||
gen(p, n[i], a)
|
||||
if a.typ == etyBaseIndex:
|
||||
r.res.addf("[$1, $2]", [a.address, a.res])
|
||||
else:
|
||||
if not needsNoCopy(p, n[i]):
|
||||
let typ = n[i].typ.skipTypes(abstractInst)
|
||||
useMagic(p, "nimCopy")
|
||||
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
|
||||
## Constructs array or sequence.
|
||||
## Nim array of uint8..uint32, int8..int32 maps to JS typed arrays.
|
||||
## Nim sequence maps to JS array.
|
||||
var t = skipTypes(n.typ, abstractInst)
|
||||
let e = elemType(t)
|
||||
let jsTyp = arrayTypeForElemType(e)
|
||||
if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0:
|
||||
# generate typed array
|
||||
# for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]`
|
||||
# TODO use `set` or loop to initialize typed array which improves performances in some situations
|
||||
var a: TCompRes
|
||||
r.res = "new $1([" % [rope(jsTyp)]
|
||||
r.kind = resExpr
|
||||
for i in 0 ..< n.len:
|
||||
if i > 0: r.res.add(", ")
|
||||
gen(p, n[i], a)
|
||||
r.res.add(a.res)
|
||||
r.res.add("]")
|
||||
r.res.add("])")
|
||||
else:
|
||||
genJSArrayConstr(p, n, r)
|
||||
|
||||
proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
@@ -2482,7 +2540,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let f = n.floatVal
|
||||
case classify(f)
|
||||
of fcNan:
|
||||
r.res = rope"NaN"
|
||||
if signbit(f):
|
||||
r.res = rope"-NaN"
|
||||
else:
|
||||
r.res = rope"NaN"
|
||||
of fcNegZero:
|
||||
r.res = rope"-0.0"
|
||||
of fcZero:
|
||||
@@ -2528,7 +2589,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let s = n[namePos].sym
|
||||
discard mangleName(p.module, s)
|
||||
r.res = s.loc.r
|
||||
if lfNoDecl in s.loc.flags or s.magic != mNone: discard
|
||||
if lfNoDecl in s.loc.flags or s.magic notin {mNone, mIsolate}: discard
|
||||
elif not p.g.generatedSyms.containsOrIncl(s.id):
|
||||
p.locals.add(genProc(p, s))
|
||||
of nkType: r.res = genTypeInfo(p, n.typ)
|
||||
@@ -2565,7 +2626,8 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkRaiseStmt: genRaiseStmt(p, n)
|
||||
of nkTypeSection, nkCommentStmt, nkIncludeStmt,
|
||||
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
||||
nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard
|
||||
nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt,
|
||||
nkMixinStmt, nkBindStmt: discard
|
||||
of nkIteratorDef:
|
||||
if n[0].sym.typ.callConv == TCallingConvention.ccClosure:
|
||||
globalError(p.config, n.info, "Closure iterators are not supported by JS backend!")
|
||||
|
||||
@@ -130,14 +130,14 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator)
|
||||
var n = newNodeI(nkRange, iter.info)
|
||||
n.add newIntNode(nkIntLit, -1)
|
||||
n.add newIntNode(nkIntLit, 0)
|
||||
result = newType(tyRange, nextId(idgen), iter)
|
||||
result = newType(tyRange, nextTypeId(idgen), iter)
|
||||
result.n = n
|
||||
var intType = nilOrSysInt(g)
|
||||
if intType.isNil: intType = newType(tyInt, nextId(idgen), iter)
|
||||
if intType.isNil: intType = newType(tyInt, nextTypeId(idgen), iter)
|
||||
rawAddSon(result, intType)
|
||||
|
||||
proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), nextId(idgen), iter, iter.info)
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info)
|
||||
result.typ = createClosureIterStateType(g, iter, idgen)
|
||||
|
||||
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
|
||||
@@ -151,7 +151,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym
|
||||
result = iter.ast[resultPos].sym
|
||||
else:
|
||||
# XXX a bit hacky:
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), nextId(idgen), iter, iter.info, {})
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {})
|
||||
result.typ = iter.typ[0]
|
||||
incl(result.flags, sfUsed)
|
||||
iter.ast.add newSymNode(result)
|
||||
@@ -238,10 +238,11 @@ proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
|
||||
result = conf.backend == backendJs and not isCompileTime
|
||||
|
||||
proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
|
||||
createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
|
||||
createTypeBoundOps(g, nil, refType, info, idgen)
|
||||
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
|
||||
owner.flags.incl sfInjectDestructors
|
||||
if owner.kind != skMacro:
|
||||
createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
|
||||
createTypeBoundOps(g, nil, refType, info, idgen)
|
||||
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
|
||||
owner.flags.incl sfInjectDestructors
|
||||
|
||||
proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||
@@ -258,7 +259,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
|
||||
env = indirectAccess(newSymNode(it), hp, hp.info)
|
||||
else:
|
||||
let e = newSym(skLet, iter.name, nextId(idgen), owner, n.info)
|
||||
let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info)
|
||||
e.typ = hp.typ
|
||||
e.flags = hp.flags
|
||||
env = newSymNode(e)
|
||||
@@ -329,7 +330,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
||||
info: TLineInfo): PType =
|
||||
result = c.ownerToType.getOrDefault(owner.id)
|
||||
if result.isNil:
|
||||
result = newType(tyRef, nextId(c.idgen), owner)
|
||||
result = newType(tyRef, nextTypeId(c.idgen), owner)
|
||||
let obj = createEnvObj(c.graph, c.idgen, owner, info)
|
||||
rawAddSon(result, obj)
|
||||
c.ownerToType[owner.id] = result
|
||||
@@ -337,7 +338,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
|
||||
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
|
||||
if optOwnedRefs in c.graph.config.globalOptions:
|
||||
assert t.kind == tyRef
|
||||
result = newType(tyOwned, nextId(c.idgen), t.owner)
|
||||
result = newType(tyOwned, nextTypeId(c.idgen), t.owner)
|
||||
result.flags.incl tfHasOwned
|
||||
result.rawAddSon t
|
||||
else:
|
||||
@@ -346,7 +347,7 @@ proc asOwnedRef(c: var DetectionPass; t: PType): PType =
|
||||
proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
|
||||
info: TLineInfo): PType =
|
||||
var r = c.getEnvTypeForOwner(owner, info)
|
||||
result = newType(tyPtr, nextId(c.idgen), owner)
|
||||
result = newType(tyPtr, nextTypeId(c.idgen), owner)
|
||||
rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
|
||||
|
||||
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
@@ -374,7 +375,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
|
||||
localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
|
||||
else:
|
||||
let result = newSym(skField, upIdent, nextId(c.idgen), obj.owner, obj.owner.info)
|
||||
let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info)
|
||||
result.typ = fieldType
|
||||
when false:
|
||||
if c.graph.config.selectedGC == gcDestructors:
|
||||
@@ -412,7 +413,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
|
||||
let t = c.getEnvTypeForOwner(owner, info)
|
||||
if cp == nil:
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextId(c.idgen), fn, fn.info)
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info)
|
||||
incl(cp.flags, sfFromGeneric)
|
||||
cp.typ = t
|
||||
addHiddenParam(fn, cp)
|
||||
@@ -496,7 +497,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
w = up
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
|
||||
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
|
||||
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr:
|
||||
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
discard
|
||||
of nkLambdaKinds, nkIteratorDef:
|
||||
if n.typ != nil:
|
||||
@@ -538,7 +540,7 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||
result = n
|
||||
|
||||
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
|
||||
var v = newSym(skVar, getIdent(cache, envName), nextId(idgen), owner, info)
|
||||
var v = newSym(skVar, getIdent(cache, envName), nextSymId(idgen), owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = typ
|
||||
result = newSymNode(v)
|
||||
@@ -562,7 +564,7 @@ proc setupEnvVar(owner: PSym; d: var DetectionPass;
|
||||
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
|
||||
c.envVars[owner.id] = result
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextId d.idgen, owner, info)
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextSymId d.idgen, owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = envVarType
|
||||
c.unownedEnvVars[owner.id] = newSymNode(v)
|
||||
@@ -613,7 +615,8 @@ proc rawClosureCreation(owner: PSym;
|
||||
let fieldAccess = indirectAccess(env, local, env.info)
|
||||
# add ``env.param = param``
|
||||
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
|
||||
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
|
||||
if owner.kind != skMacro:
|
||||
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
|
||||
if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
|
||||
owner.flags.incl sfInjectDestructors
|
||||
|
||||
@@ -645,7 +648,7 @@ proc closureCreationForIter(iter: PNode;
|
||||
d: var DetectionPass; c: var LiftingPass): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), nextId(d.idgen), owner, iter.info)
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), nextSymId(d.idgen), owner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
@@ -750,7 +753,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
result = accessViaEnvVar(n, owner, d, c)
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
|
||||
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef,
|
||||
nkMacroDef, nkFuncDef:
|
||||
nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt:
|
||||
discard
|
||||
of nkClosure:
|
||||
if n[1].kind == nkNilLit:
|
||||
@@ -935,7 +938,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
let iter = op.sym
|
||||
|
||||
let hp = getHiddenParam(g, iter)
|
||||
env = newSym(skLet, iter.name, nextId(idgen), owner, body.info)
|
||||
env = newSym(skLet, iter.name, nextSymId(idgen), owner, body.info)
|
||||
env.typ = hp.typ
|
||||
env.flags = hp.flags
|
||||
|
||||
|
||||
@@ -514,7 +514,8 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
(not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
|
||||
wrSpace em
|
||||
wr(em, $tok.tokType, ltSomeParLe)
|
||||
rememberSplit(splitParLe)
|
||||
if tok.tokType != tkCurlyDotLe:
|
||||
rememberSplit(splitParLe)
|
||||
of closedPars:
|
||||
wr(em, $tok.tokType, ltSomeParRi)
|
||||
of tkColonColon:
|
||||
|
||||
@@ -955,8 +955,10 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
|
||||
# detect the amount of indentation:
|
||||
if isDoc:
|
||||
toStrip = getColNumber(L, pos)
|
||||
while L.buf[pos] == ' ': inc pos
|
||||
if L.buf[pos] in {CR, LF}:
|
||||
while L.buf[pos] == ' ':
|
||||
inc pos
|
||||
inc toStrip
|
||||
while L.buf[pos] in {CR, LF}: # skip blank lines
|
||||
pos = handleCRLF(L, pos)
|
||||
toStrip = 0
|
||||
while L.buf[pos] == ' ':
|
||||
@@ -1028,11 +1030,17 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||
inc(pos, 2)
|
||||
|
||||
var toStrip = 0
|
||||
while L.buf[pos] == ' ':
|
||||
inc pos
|
||||
inc toStrip
|
||||
var stripInit = false
|
||||
|
||||
while true:
|
||||
if not stripInit: # find baseline indentation inside comment
|
||||
while L.buf[pos] == ' ':
|
||||
inc pos
|
||||
inc toStrip
|
||||
if L.buf[pos] in {CR, LF}: # don't set toStrip in blank comment lines
|
||||
toStrip = 0
|
||||
else: # found first non-whitespace character
|
||||
stripInit = true
|
||||
var lastBackslash = -1
|
||||
while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
|
||||
if L.buf[pos] == '\\': lastBackslash = pos+1
|
||||
@@ -1048,11 +1056,12 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||
if L.buf[pos] == '#' and L.buf[pos+1] == '#':
|
||||
tok.literal.add "\n"
|
||||
inc(pos, 2)
|
||||
var c = toStrip
|
||||
while L.buf[pos] == ' ' and c > 0:
|
||||
inc pos
|
||||
dec c
|
||||
inc tok.iNumber
|
||||
if stripInit:
|
||||
var c = toStrip
|
||||
while L.buf[pos] == ' ' and c > 0:
|
||||
inc pos
|
||||
dec c
|
||||
inc tok.iNumber
|
||||
else:
|
||||
if L.buf[pos] > ' ':
|
||||
L.indentAhead = indent
|
||||
|
||||
@@ -29,6 +29,10 @@ type
|
||||
c: PContext # c can be nil, then we are called from lambdalifting!
|
||||
idgen: IdGenerator
|
||||
|
||||
template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
|
||||
template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
|
||||
template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym
|
||||
@@ -42,8 +46,9 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result[1] = i
|
||||
result.typ = elemType
|
||||
|
||||
proc destructorOverriden(t: PType): bool =
|
||||
t.attachedOps[attachedDestructor] != nil and sfOverriden in t.attachedOps[attachedDestructor].flags
|
||||
proc destructorOverriden(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
op != nil and sfOverriden in op.flags
|
||||
|
||||
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
for i in 0..<t.len:
|
||||
@@ -65,16 +70,19 @@ proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
result[0] = le
|
||||
result[1] = ri
|
||||
|
||||
proc genBuiltin*(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
|
||||
proc genBuiltin*(g: ModuleGraph; idgen: IdGenerator; magic: TMagic; name: string; i: PNode): PNode =
|
||||
result = newNodeI(nkCall, i.info)
|
||||
result.add createMagic(g, name, magic).newSymNode
|
||||
result.add createMagic(g, idgen, name, magic).newSymNode
|
||||
result.add i
|
||||
|
||||
proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode =
|
||||
result = genBuiltin(c.g, c.idgen, magic, name, i)
|
||||
|
||||
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
elif c.kind == attachedDestructor and c.addMemReset:
|
||||
let call = genBuiltin(c.g, mDefault, "default", x)
|
||||
let call = genBuiltin(c, mDefault, "default", x)
|
||||
call.typ = t
|
||||
body.add newAsgnStmt(x, call)
|
||||
|
||||
@@ -88,7 +96,7 @@ proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
||||
|
||||
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c.g, mLtI, "<", i)
|
||||
let cmp = genBuiltin(c, mLtI, "<", i)
|
||||
cmp.add genLen(c.g, dest)
|
||||
cmp.typ = getSysType(c.g, c.info, tyBool)
|
||||
result[0] = cmp
|
||||
@@ -111,10 +119,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
|
||||
dotExpr.add newNodeIT(nkType, c.info, objType)
|
||||
dotExpr.add newSymNode(field)
|
||||
|
||||
let offsetOf = genBuiltin(c.g, mOffsetOf, "offsetof", dotExpr)
|
||||
let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
|
||||
offsetOf.typ = intType
|
||||
|
||||
let minusExpr = genBuiltin(c.g, mSubI, "-", castExpr1)
|
||||
let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
|
||||
minusExpr.typ = intType
|
||||
minusExpr.add offsetOf
|
||||
|
||||
@@ -130,7 +138,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
if sfNeverRaises notin op.flags:
|
||||
c.canRaise = true
|
||||
if c.addMemReset:
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c.g, mWasMoved, "wasMoved", x))
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
|
||||
else:
|
||||
result = destroy
|
||||
|
||||
@@ -222,7 +230,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
# for every field (dependent on dest.kind):
|
||||
# `=` dest.field, src.field
|
||||
# =destroy(blob)
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId 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 = x.typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
@@ -232,7 +240,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
#body.add newAsgnStmt(blob, x)
|
||||
|
||||
var wasMovedCall = newNodeI(nkCall, c.info)
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, "wasMoved", mWasMoved)))
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
|
||||
wasMovedCall.add x # mWasMoved does not take the address
|
||||
body.add wasMovedCall
|
||||
|
||||
@@ -316,8 +324,8 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
field: var PSym): bool =
|
||||
if optSeqDestructors in c.g.config.globalOptions:
|
||||
var op = field
|
||||
let destructorOverriden = destructorOverriden(t)
|
||||
if op != nil and op != c.fn and
|
||||
let destructorOverriden = destructorOverriden(c.g, t)
|
||||
if op != nil and op != c.fn and
|
||||
(sfOverriden in op.flags or destructorOverriden):
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
@@ -349,7 +357,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
# We also now do generic instantiations in the destructor lifting pass:
|
||||
if op.ast[genericParamsPos].kind != nkEmpty:
|
||||
if op.ast.isGenericRoutine:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
field = op
|
||||
#echo "trying to use ", op.ast
|
||||
@@ -361,14 +369,14 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
result = true
|
||||
|
||||
proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
|
||||
let t = orig.skipTypes(abstractInst)
|
||||
let t = orig.skipTypes(abstractInst - {tyDistinct})
|
||||
var op = t.destructor
|
||||
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op.ast[genericParamsPos].kind != nkEmpty:
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
t.attachedOps[attachedDestructor] = op
|
||||
setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
|
||||
|
||||
if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
|
||||
op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
|
||||
@@ -389,10 +397,10 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
var op = t.destructor
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
|
||||
if op.ast[genericParamsPos].kind != nkEmpty:
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
t.attachedOps[attachedDestructor] = op
|
||||
setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
|
||||
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
@@ -400,11 +408,17 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
result = true
|
||||
#result = addDestructorCall(c, t, body, x)
|
||||
of attachedAsgn, attachedSink, attachedTrace:
|
||||
result = considerAsgnOrSink(c, t, body, x, y, t.attachedOps[c.kind])
|
||||
var op = getAttachedOp(c.g, t, c.kind)
|
||||
result = considerAsgnOrSink(c, t, body, x, y, op)
|
||||
if op != nil:
|
||||
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
|
||||
of attachedDispose:
|
||||
result = considerAsgnOrSink(c, t, body, x, nil, t.attachedOps[c.kind])
|
||||
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:
|
||||
let op = t.attachedOps[attachedDeepCopy]
|
||||
let op = getAttachedOp(c.g, t, attachedDeepCopy)
|
||||
if op != nil:
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
@@ -412,7 +426,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
result = true
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(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)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
@@ -422,7 +436,7 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
body.add v
|
||||
|
||||
proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(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 = value.typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
@@ -432,25 +446,25 @@ proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
|
||||
body.add v
|
||||
|
||||
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
||||
let incCall = genBuiltin(c.g, mInc, "inc", i)
|
||||
let incCall = genBuiltin(c, mInc, "inc", i)
|
||||
incCall.add lowerings.newIntLit(c.g, c.info, 1)
|
||||
body.add incCall
|
||||
|
||||
proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
|
||||
proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
# don't call genAddr(c, x) here:
|
||||
result = genBuiltin(g, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(g, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(g, x.info, tyInt)
|
||||
result = genBuiltin(c, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
result.add lenCall
|
||||
|
||||
proc setLenStrCall(g: ModuleGraph; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(g, mLengthStr, "len", y)
|
||||
lenCall.typ = getSysType(g, x.info, tyInt)
|
||||
result = genBuiltin(g, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthStr, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||
result.add lenCall
|
||||
|
||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c.g, mLengthSeq, "len", y)
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
|
||||
op = instantiateGeneric(c, op, t, t)
|
||||
@@ -476,7 +490,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c, t.destructor, x)
|
||||
@@ -484,13 +498,13 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDestructor:
|
||||
# destroy all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
# follow all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedDispose:
|
||||
forallElements(c, t, body, x, y)
|
||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||
@@ -510,7 +524,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newHookCall(c, t.assignment, x, y)
|
||||
of attachedSink:
|
||||
# we always inline the move for better performance:
|
||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c, t.destructor, x)
|
||||
@@ -523,26 +537,28 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
doAssert t.destructor != nil
|
||||
body.add destructorCall(c, t.destructor, x)
|
||||
of attachedTrace:
|
||||
if t.attachedOps[c.kind] == nil:
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, t.attachedOps[c.kind], x, y)
|
||||
body.add newHookCall(c, op, x, y)
|
||||
of attachedDispose:
|
||||
if t.attachedOps[c.kind] == nil:
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, t.attachedOps[c.kind], x, nil)
|
||||
body.add newHookCall(c, op, x, nil)
|
||||
|
||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c, t.destructor, x)
|
||||
body.add moveCall
|
||||
of attachedDestructor, attachedDispose:
|
||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
discard "strings are atomic and have no inner elements that are to trace"
|
||||
|
||||
@@ -586,7 +602,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
|
||||
else:
|
||||
@@ -596,7 +612,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var cond: PNode
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||
else:
|
||||
@@ -628,7 +644,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
|
||||
else:
|
||||
@@ -646,7 +662,7 @@ proc atomicRefOp(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
|
||||
## and we need to do the refcounting only on the ref field which we call 'xenv':
|
||||
let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||
@@ -674,7 +690,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, cond, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
@@ -728,11 +744,11 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
let elemType = t.lastSon
|
||||
#fillBody(c, elemType, actions, genDeref(x), genDeref(y))
|
||||
#var disposeCall = genBuiltin(c.g, mDispose, "dispose", x)
|
||||
#var disposeCall = genBuiltin(c, mDispose, "dispose", x)
|
||||
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
|
||||
else:
|
||||
@@ -754,12 +770,12 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# have to go through some indirection; we delegate this to the codegen:
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
call.typ = t
|
||||
call[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
|
||||
call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
|
||||
call[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -768,7 +784,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
@@ -783,7 +799,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedTrace, attachedDispose: discard
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
|
||||
@@ -846,7 +862,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
discard considerUserDefinedOp(c, t, body, x, y)
|
||||
elif tfHasAsgn in t.flags:
|
||||
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
|
||||
body.add newSeqCall(c.g, x, y)
|
||||
body.add newSeqCall(c, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
@@ -883,33 +899,33 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of tyOrdinal, tyRange, tyInferred,
|
||||
tyGenericInst, tyAlias, tySink:
|
||||
fillBody(c, lastSon(t), body, x, y)
|
||||
of tyOptDeprecated: doAssert false
|
||||
of tyConcept: doAssert false
|
||||
|
||||
proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
kind: TTypeAttachedOp; info: TLineInfo;
|
||||
idgen: IdGenerator): PSym =
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
if baseType.attachedOps[kind] == nil:
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
typ.attachedOps[kind] = baseType.attachedOps[kind]
|
||||
result = typ.attachedOps[kind]
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
result = newSym(skProc, procname, nextId(idgen), owner, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), nextId(idgen), result, info)
|
||||
result = newSym(skProc, procname, nextSymId(idgen), owner, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
|
||||
nextId(idgen), result, info)
|
||||
nextSymId(idgen), result, info)
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
if kind == attachedTrace:
|
||||
src.typ = getSysType(g, info, tyPointer)
|
||||
else:
|
||||
src.typ = typ
|
||||
|
||||
result.typ = newProcType(info, nextId(idgen), owner)
|
||||
result.typ = newProcType(info, nextTypeId(idgen), owner)
|
||||
result.typ.addParam dest
|
||||
if kind notin {attachedDestructor, attachedDispose}:
|
||||
result.typ.addParam src
|
||||
@@ -917,7 +933,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
|
||||
cyclicType(typ.skipTypes(abstractInst)):
|
||||
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
|
||||
nextId(idgen), result, info)
|
||||
nextSymId(idgen), result, info)
|
||||
cycleParam.typ = getSysType(g, info, tyBool)
|
||||
result.typ.addParam cycleParam
|
||||
|
||||
@@ -936,7 +952,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(g, c, typ, kind, info, idgen)
|
||||
|
||||
result = typ.attachedOps[kind]
|
||||
result = getAttachedOp(g, typ, kind)
|
||||
if result == nil:
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
|
||||
|
||||
@@ -949,12 +965,12 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
else: newSymNode(result.typ.n[2].sym)
|
||||
|
||||
# register this operation already:
|
||||
typ.attachedOps[kind] = result
|
||||
setAttachedOpPartial(g, idgen.module, typ, kind, result)
|
||||
|
||||
if kind == attachedSink and destructorOverriden(typ):
|
||||
if kind == attachedSink and destructorOverriden(g, typ):
|
||||
## compiler can use a combination of `=destroy` and memCopy for sink op
|
||||
dest.flags.incl sfCursor
|
||||
result.ast[bodyPos].add newOpCall(a, typ.attachedOps[attachedDestructor], d[0])
|
||||
result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
else:
|
||||
var tk: TTypeKind
|
||||
@@ -970,6 +986,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
else:
|
||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||
if not a.canRaise: incl result.flags, sfNeverRaises
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
@@ -983,7 +1000,7 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
a.addMemReset = true
|
||||
let discrimantDest = result.typ.n[1].sym
|
||||
|
||||
let dst = newSym(skVar, getIdent(g.cache, "dest"), nextId(idgen), result, info)
|
||||
let dst = newSym(skVar, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
|
||||
dst.typ = makePtrType(typ.owner, typ, idgen)
|
||||
let dstSym = newSymNode(dst)
|
||||
let d = newDeref(dstSym)
|
||||
@@ -1002,30 +1019,34 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
|
||||
if n.kind in nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
if t.destructor == nil:
|
||||
if getAttachedOp(g, t, attachedDestructor) == nil:
|
||||
discard produceSym(g, c, t, attachedDestructor, info, idgen)
|
||||
|
||||
if t.destructor != nil:
|
||||
if t.destructor.ast[genericParamsPos].kind != nkEmpty:
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
if op != nil:
|
||||
if op.ast.isGenericRoutine:
|
||||
internalError(g.config, info, "resolved destructor is generic")
|
||||
if t.destructor.magic == mDestroy:
|
||||
if op.magic == mDestroy:
|
||||
internalError(g.config, info, "patching mDestroy with mDestroy?")
|
||||
n[0] = newSymNode(t.destructor)
|
||||
n[0] = newSymNode(op)
|
||||
for x in n: patchBody(g, c, x, info, idgen)
|
||||
|
||||
template inst(field, t, idgen) =
|
||||
if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
|
||||
proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator;
|
||||
info: TLineInfo) =
|
||||
let op = getAttachedOp(g, t, kind)
|
||||
if op != nil and op.ast != nil and op.ast.isGenericRoutine:
|
||||
if t.typeInst != nil:
|
||||
var a: TLiftCtx
|
||||
a.info = info
|
||||
a.g = g
|
||||
a.kind = k
|
||||
a.kind = kind
|
||||
a.c = c
|
||||
a.idgen = idgen
|
||||
|
||||
field = instantiateGeneric(a, field, t, t.typeInst)
|
||||
if field.ast != nil:
|
||||
patchBody(g, c, field.ast, info, a.idgen)
|
||||
let opInst = instantiateGeneric(a, op, t, t.typeInst)
|
||||
if opInst.ast != nil:
|
||||
patchBody(g, c, opInst.ast, info, a.idgen)
|
||||
setAttachedOp(g, idgen.module, t, kind, opInst)
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
@@ -1062,24 +1083,26 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
else: attachedSink
|
||||
|
||||
# bug #15122: We need to produce all prototypes before entering the
|
||||
# mind boggling recursion. Hacks like these imply we shoule rewrite
|
||||
# mind boggling recursion. Hacks like these imply we should rewrite
|
||||
# this module.
|
||||
var generics: array[attachedDestructor..attachedDispose, bool]
|
||||
for k in attachedDestructor..lastAttached:
|
||||
generics[k] = canon.attachedOps[k] != nil
|
||||
generics[k] = getAttachedOp(g, canon, k) != nil
|
||||
if not generics[k]:
|
||||
canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info, idgen)
|
||||
setAttachedOp(g, idgen.module, canon, k,
|
||||
symPrototype(g, canon, canon.owner, k, info, idgen))
|
||||
|
||||
# we generate the destructor first so that other operators can depend on it:
|
||||
for k in attachedDestructor..lastAttached:
|
||||
if not generics[k]:
|
||||
discard produceSym(g, c, canon, k, info, idgen)
|
||||
else:
|
||||
inst(canon.attachedOps[k], canon, idgen)
|
||||
inst(g, c, canon, k, idgen, info)
|
||||
if canon != orig:
|
||||
orig.attachedOps[k] = canon.attachedOps[k]
|
||||
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
|
||||
|
||||
if not isTrival(orig.destructor):
|
||||
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrival(orig.assignment) or
|
||||
# not isTrival(orig.sink):
|
||||
orig.flags.incl tfHasAsgn
|
||||
# ^ XXX Breaks IC!
|
||||
|
||||
@@ -43,7 +43,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
|
||||
of nkSym:
|
||||
if interestingVar(it.sym):
|
||||
n[i] = lookupOrAdd(c, it.sym, it.info)
|
||||
of procDefs, nkTypeSection: discard
|
||||
of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard
|
||||
else:
|
||||
for i in 0..<it.safeLen:
|
||||
liftLocals(it, i, c)
|
||||
|
||||
@@ -34,6 +34,7 @@ type
|
||||
errGeneralParseError,
|
||||
errNewSectionExpected,
|
||||
errInvalidDirectiveX,
|
||||
errFootnoteMismatch,
|
||||
errProveInit, # deadcode
|
||||
errGenerated,
|
||||
errUser,
|
||||
@@ -43,8 +44,10 @@ type
|
||||
warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
|
||||
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
|
||||
warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX",
|
||||
warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported",
|
||||
warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam",
|
||||
warnLanguageXNotSupported = "LanguageXNotSupported",
|
||||
warnFieldXNotSupported = "FieldXNotSupported",
|
||||
warnRstStyle = "warnRstStyle", warnCommentXIgnored = "CommentXIgnored",
|
||||
warnTypelessParam = "TypelessParam",
|
||||
warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
|
||||
warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
|
||||
warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple",
|
||||
@@ -56,6 +59,9 @@ type
|
||||
warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed",
|
||||
warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition",
|
||||
warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
|
||||
warnStrictNotNil = "StrictNotNil",
|
||||
warnCannotOpen = "CannotOpen",
|
||||
warnFileChanged = "FileChanged",
|
||||
warnUser = "User",
|
||||
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC",
|
||||
@@ -82,8 +88,9 @@ const
|
||||
errGridTableNotImplemented: "grid table is not implemented",
|
||||
errMarkdownIllformedTable: "illformed delimiter row of a markdown table",
|
||||
errGeneralParseError: "general parse error",
|
||||
errNewSectionExpected: "new section expected",
|
||||
errNewSectionExpected: "new section expected $1",
|
||||
errInvalidDirectiveX: "invalid directive: '$1'",
|
||||
errFootnoteMismatch: "number of footnotes and their references don't match: $1",
|
||||
errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
|
||||
errGenerated: "$1",
|
||||
errUser: "$1",
|
||||
@@ -99,6 +106,7 @@ const
|
||||
warnUnknownSubstitutionX: "unknown substitution '$1'",
|
||||
warnLanguageXNotSupported: "language '$1' not supported",
|
||||
warnFieldXNotSupported: "field '$1' not supported",
|
||||
warnRstStyle: "RST style: $1",
|
||||
warnCommentXIgnored: "comment '$1' ignored",
|
||||
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
|
||||
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
|
||||
@@ -128,6 +136,9 @@ const
|
||||
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
||||
warnCycleCreated: "$1",
|
||||
warnObservableStores: "observable stores to '$1'",
|
||||
warnStrictNotNil: "$1",
|
||||
warnCannotOpen: "cannot open: $1",
|
||||
warnFileChanged: "file changed: $1",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
@@ -278,3 +289,4 @@ proc initMsgConfig*(): MsgConfig =
|
||||
result.filenameToIndexTbl = initTable[string, FileIndex]()
|
||||
result.fileInfos = @[]
|
||||
result.errorOutputs = {eStdOut, eStdErr}
|
||||
result.filenameToIndexTbl["???"] = FileIndex(-1)
|
||||
|
||||
@@ -55,7 +55,9 @@ proc beautifyName(s: string, k: TSymKind): string =
|
||||
inc i
|
||||
while i < s.len:
|
||||
if s[i] == '_':
|
||||
if i > 0 and s[i-1] in {'A'..'Z'}:
|
||||
if i+1 >= s.len:
|
||||
discard "trailing underscores should be stripped off"
|
||||
elif i > 0 and s[i-1] in {'A'..'Z'}:
|
||||
# don't skip '_' as it's essential for e.g. 'GC_disable'
|
||||
result.add('_')
|
||||
inc i
|
||||
|
||||
@@ -20,6 +20,7 @@ when hasRstdin: import rdstdin
|
||||
|
||||
type
|
||||
TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
|
||||
OnPrompt* = proc() {.closure.}
|
||||
TLLStreamKind* = enum # enum of different stream implementations
|
||||
llsNone, # null stream: reading and writing has no effect
|
||||
llsString, # stream encapsulates a string
|
||||
@@ -32,6 +33,7 @@ type
|
||||
rd*, wr*: int # for string streams
|
||||
lineOffset*: int # for fake stdin line numbers
|
||||
repl*: TLLRepl # gives stdin control to clients
|
||||
onPrompt*: OnPrompt
|
||||
|
||||
PLLStream* = ref TLLStream
|
||||
|
||||
@@ -55,12 +57,13 @@ proc llStreamOpen*(): PLLStream =
|
||||
result.kind = llsNone
|
||||
|
||||
proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
|
||||
proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin): PLLStream =
|
||||
proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream =
|
||||
new(result)
|
||||
result.kind = llsStdIn
|
||||
result.s = ""
|
||||
result.lineOffset = -1
|
||||
result.repl = r
|
||||
result.onPrompt = onPrompt
|
||||
|
||||
proc llStreamClose*(s: PLLStream) =
|
||||
case s.kind
|
||||
@@ -72,10 +75,10 @@ proc llStreamClose*(s: PLLStream) =
|
||||
when not declared(readLineFromStdin):
|
||||
# fallback implementation:
|
||||
proc readLineFromStdin(prompt: string, line: var string): bool =
|
||||
stderr.write(prompt)
|
||||
stdout.write(prompt)
|
||||
result = readLine(stdin, line)
|
||||
if not result:
|
||||
stderr.write("\n")
|
||||
stdout.write("\n")
|
||||
quit(0)
|
||||
|
||||
proc endsWith*(x: string, s: set[char]): bool =
|
||||
@@ -133,6 +136,7 @@ proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
|
||||
of llsFile:
|
||||
result = readBuffer(s.f, buf, bufLen)
|
||||
of llsStdIn:
|
||||
if s.onPrompt!=nil: s.onPrompt()
|
||||
result = s.repl(s, buf, bufLen)
|
||||
|
||||
proc llStreamReadLine*(s: PLLStream, line: var string): bool =
|
||||
@@ -143,11 +147,11 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool =
|
||||
of llsString:
|
||||
while s.rd < s.s.len:
|
||||
case s.s[s.rd]
|
||||
of '\x0D':
|
||||
of '\r':
|
||||
inc(s.rd)
|
||||
if s.s[s.rd] == '\x0A': inc(s.rd)
|
||||
if s.s[s.rd] == '\n': inc(s.rd)
|
||||
break
|
||||
of '\x0A':
|
||||
of '\n':
|
||||
inc(s.rd)
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
|
||||
import
|
||||
intsets, ast, astalgo, idents, semdata, types, msgs, options,
|
||||
renderer, nimfix/prettybase, lineinfos, strutils
|
||||
renderer, nimfix/prettybase, lineinfos, strutils,
|
||||
modulegraphs
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
|
||||
|
||||
@@ -107,8 +108,9 @@ proc localSearchInScope*(c: PContext, s: PIdent): PSym =
|
||||
scope = scope.parent
|
||||
result = strTableGet(scope.symbols, s)
|
||||
|
||||
proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; name: PIdent): PSym =
|
||||
result = initIdentIter(ti, im.m.tab, name)
|
||||
proc initIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; name: PIdent;
|
||||
g: ModuleGraph): PSym =
|
||||
result = initModuleIter(ti, g, im.m, name)
|
||||
while result != nil:
|
||||
let b =
|
||||
case im.mode
|
||||
@@ -117,11 +119,12 @@ proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; n
|
||||
of importExcept: name.id notin im.exceptSet
|
||||
if b and not containsOrIncl(marked, result.id):
|
||||
return result
|
||||
result = nextIdentIter(ti, im.m.tab)
|
||||
result = nextModuleIter(ti, g)
|
||||
|
||||
proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule): PSym =
|
||||
proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
|
||||
g: ModuleGraph): PSym =
|
||||
while true:
|
||||
result = nextIdentIter(ti, im.m.tab)
|
||||
result = nextModuleIter(ti, g)
|
||||
if result == nil: return nil
|
||||
case im.mode
|
||||
of importAll:
|
||||
@@ -134,17 +137,17 @@ proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule):
|
||||
if result.name.id notin im.exceptSet and not containsOrIncl(marked, result.id):
|
||||
return result
|
||||
|
||||
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent): PSym =
|
||||
var ti: TIdentIter
|
||||
var candidate = initIdentIter(ti, marked, im, name)
|
||||
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
|
||||
var ti: ModuleIter
|
||||
var candidate = initIdentIter(ti, marked, im, name, g)
|
||||
while candidate != nil:
|
||||
yield candidate
|
||||
candidate = nextIdentIter(ti, marked, im)
|
||||
candidate = nextIdentIter(ti, marked, im, g)
|
||||
|
||||
iterator importedItems*(c: PContext; name: PIdent): PSym =
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, name):
|
||||
for s in symbols(im, marked, name, c.graph):
|
||||
yield s
|
||||
|
||||
proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
|
||||
@@ -169,15 +172,15 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
|
||||
dec scopeN
|
||||
isLocal = false
|
||||
for im in c.imports.mitems:
|
||||
for s in im.m.tab.data:
|
||||
if s != nil:
|
||||
yield (s, scopeN, isLocal)
|
||||
for s in modulegraphs.allSyms(c.graph, im.m):
|
||||
assert s != nil
|
||||
yield (s, scopeN, isLocal)
|
||||
|
||||
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
|
||||
var marked = initIntSet()
|
||||
result = nil
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, name):
|
||||
for s in symbols(im, marked, name, c.graph):
|
||||
if result == nil:
|
||||
result = s
|
||||
else:
|
||||
@@ -190,14 +193,17 @@ proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
|
||||
if result != nil: return result
|
||||
result = someSymFromImportTable(c, s, ambiguous)
|
||||
|
||||
proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
|
||||
proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
var i = 0
|
||||
var count = 0
|
||||
for scope in allScopes(c.currentScope):
|
||||
echo "scope ", i
|
||||
for h in 0..high(scope.symbols.data):
|
||||
if scope.symbols.data[h] != nil:
|
||||
echo scope.symbols.data[h].name.s
|
||||
if i == limit: break
|
||||
if count >= max: return
|
||||
echo count, ": ", scope.symbols.data[h].name.s
|
||||
count.inc
|
||||
if i == limit: return
|
||||
inc i
|
||||
|
||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
@@ -214,7 +220,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
if result.len == 0:
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s):
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
@@ -227,7 +233,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
considerQuotedIdent(c, m)
|
||||
else:
|
||||
getIdent(c.cache, "err:" & renderTree(m))
|
||||
result = newSym(skError, ident, nextId(c.idgen), getCurrOwner(c), n.info, {})
|
||||
result = newSym(skError, ident, nextSymId(c.idgen), getCurrOwner(c), n.info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
@@ -240,6 +246,7 @@ type
|
||||
oimSymChoiceLocalLookup
|
||||
TOverloadIter* = object
|
||||
it*: TIdentIter
|
||||
mit*: ModuleIter
|
||||
m*: PSym
|
||||
mode*: TOverloadIterMode
|
||||
symChoiceIndex*: int
|
||||
@@ -280,6 +287,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||
|
||||
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
||||
conflictsWith: TLineInfo) =
|
||||
## Emit a redefinition error if in non-interactive mode
|
||||
if c.config.cmd != cmdInteractive:
|
||||
localError(c.config, info,
|
||||
"redefinition of '$1'; previous declaration here: $2" %
|
||||
@@ -306,7 +314,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) =
|
||||
proc addInterfaceDeclAux(c: PContext, sym: PSym) =
|
||||
if sfExported in sym.flags:
|
||||
# add to interface:
|
||||
if c.module != nil: strTableAdd(c.module.tab, sym)
|
||||
if c.module != nil: exportSym(c, sym)
|
||||
else: internalError(c.config, sym.info, "addInterfaceDeclAux")
|
||||
|
||||
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
|
||||
@@ -350,33 +358,92 @@ proc mergeShadowScope*(c: PContext) =
|
||||
else:
|
||||
c.addInterfaceDecl(sym)
|
||||
|
||||
when defined(nimfix):
|
||||
# when we cannot find the identifier, retry with a changed identifier:
|
||||
proc altSpelling(x: PIdent): PIdent =
|
||||
when false:
|
||||
# `nimfix` used to call `altSpelling` and prettybase.replaceDeprecated(n.info, ident, alt)
|
||||
proc altSpelling(c: PContext, x: PIdent): PIdent =
|
||||
case x.s[0]
|
||||
of 'A'..'Z': result = getIdent(toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
of 'a'..'z': result = getIdent(toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
of 'A'..'Z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
of 'a'..'z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
else: result = x
|
||||
|
||||
template fixSpelling(n: PNode; ident: PIdent; op: untyped) =
|
||||
let alt = ident.altSpelling
|
||||
result = op(c, alt).skipAlias(n)
|
||||
if result != nil:
|
||||
prettybase.replaceDeprecated(n.info, ident, alt)
|
||||
return result
|
||||
else:
|
||||
template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard
|
||||
import std/[editdistance, heapqueue]
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
type SpellCandidate = object
|
||||
dist: int
|
||||
depth: int
|
||||
msg: string
|
||||
sym: PSym
|
||||
|
||||
template toOrderTup(a: SpellCandidate): auto =
|
||||
# `dist` is first, to favor nearby matches
|
||||
# `depth` is next, to favor nearby enclosing scopes among ties
|
||||
# `sym.name.s` is last, to make the list ordered and deterministic among ties
|
||||
(a.dist, a.depth, a.msg)
|
||||
|
||||
proc `<`(a, b: SpellCandidate): bool =
|
||||
a.toOrderTup < b.toOrderTup
|
||||
|
||||
proc mustFixSpelling(c: PContext): bool {.inline.} =
|
||||
result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
|
||||
# don't slowdown inside compiles()
|
||||
|
||||
proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||
## when we cannot find the identifier, suggest nearby spellings
|
||||
var list = initHeapQueue[SpellCandidate]()
|
||||
let name0 = ident.s.nimIdentNormalize
|
||||
|
||||
for (sym, depth, isLocal) in allSyms(c):
|
||||
let depth = -depth - 1
|
||||
let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
|
||||
var msg: string
|
||||
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)
|
||||
|
||||
if list.len == 0: return
|
||||
let e0 = list[0]
|
||||
var count = 0
|
||||
while true:
|
||||
# pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`.
|
||||
if list.len == 0: break
|
||||
let e = list.pop()
|
||||
if c.config.spellSuggestMax == spellSuggestSecretSauce:
|
||||
const
|
||||
smallThres = 2
|
||||
maxCountForSmall = 4
|
||||
# avoids ton of operator matches when mis-matching short symbols such as `i`
|
||||
# other heuristics could be devised, such as only suggesting operators if `name0`
|
||||
# is an operator (likewise with non-operators).
|
||||
if e.dist > e0.dist or (name0.len <= smallThres and count >= maxCountForSmall): break
|
||||
elif count >= c.config.spellSuggestMax: break
|
||||
if count == 0:
|
||||
result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
|
||||
result.add e.msg
|
||||
count.inc
|
||||
|
||||
proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym =
|
||||
var err = "ambiguous identifier: '" & s.name.s & "'"
|
||||
var i = 0
|
||||
var ignoredModules = 0
|
||||
for candidate in importedItems(c, s.name):
|
||||
if i == 0: err.add " -- use one of the following:\n"
|
||||
else: err.add "\n"
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ)
|
||||
if candidate.kind == skModule:
|
||||
inc ignoredModules
|
||||
else:
|
||||
result = candidate
|
||||
inc i
|
||||
localError(c.config, info, errGenerated, err)
|
||||
if ignoredModules != i-1:
|
||||
localError(c.config, info, errGenerated, err)
|
||||
result = nil
|
||||
else:
|
||||
amb = false
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
var amb: bool
|
||||
discard errorUseQualifier(c, info, s, amb)
|
||||
|
||||
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
|
||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
@@ -389,8 +456,8 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
|
||||
inc i
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
|
||||
var err = "undeclared identifier: '" & name & "'"
|
||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
||||
var err = "undeclared identifier: '" & name & "'" & extra
|
||||
if c.recursiveDep.len > 0:
|
||||
err.add "\nThis might be caused by a recursive module dependency:\n"
|
||||
err.add c.recursiveDep
|
||||
@@ -398,31 +465,31 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
|
||||
c.recursiveDep = ""
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
|
||||
var extra = ""
|
||||
if c.mustFixSpelling: fixSpelling(c, n, ident, extra)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s, extra)
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
# Looks up a symbol. Generates an error in case of nil.
|
||||
var amb = false
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
fixSpelling(n, n.ident, searchInScopes)
|
||||
errorUndeclaredIdentifier(c, n.info, n.ident.s)
|
||||
result = errorSym(c, n)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkAccQuoted:
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
fixSpelling(n, ident, searchInScopes)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
||||
result = errorSym(c, n)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
else:
|
||||
internalError(c.config, n.info, "lookUp")
|
||||
return
|
||||
if amb:
|
||||
#contains(c.ambiguousSymbols, result.id):
|
||||
errorUseQualifier(c, n.info, result)
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
when false:
|
||||
if result.kind == skStub: loadStub(result)
|
||||
|
||||
@@ -454,11 +521,9 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
|
||||
if result == nil and checkUndeclared in flags:
|
||||
fixSpelling(n, ident, searchInScopes)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
||||
result = errorSym(c, n)
|
||||
result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
elif checkAmbiguity in flags and result != nil and amb:
|
||||
errorUseQualifier(c, n.info, result)
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
c.isAmbiguous = amb
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
@@ -475,11 +540,9 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
if m == c.module:
|
||||
result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
|
||||
else:
|
||||
result = strTableGet(m.tab, ident).skipAlias(n, c.config)
|
||||
result = someSym(c.graph, m, ident).skipAlias(n, c.config)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
fixSpelling(n[1], ident, searchInScopes)
|
||||
errorUndeclaredIdentifier(c, n[1].info, ident.s)
|
||||
result = errorSym(c, n[1])
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], ident)
|
||||
elif n[1].kind == nkSym:
|
||||
result = n[1].sym
|
||||
elif checkUndeclared in flags and
|
||||
@@ -509,7 +572,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
scope = scope.parent
|
||||
if scope == nil:
|
||||
for i in 0..c.imports.high:
|
||||
result = initIdentIter(o.it, o.marked, c.imports[i], ident).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config)
|
||||
if result != nil:
|
||||
o.currentScope = nil
|
||||
o.importIdx = i
|
||||
@@ -535,7 +598,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
ident).skipAlias(n, c.config)
|
||||
o.mode = oimSelfModule
|
||||
else:
|
||||
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config)
|
||||
result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config)
|
||||
else:
|
||||
noidentError(c.config, n[1], n)
|
||||
result = errorSym(c, n[1])
|
||||
@@ -568,7 +631,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
|
||||
var idx = o.importIdx+1
|
||||
o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
|
||||
while idx < c.imports.len:
|
||||
result = initIdentIter(o.it, o.marked, c.imports[idx], o.it.name).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
if result != nil:
|
||||
# oh, we were wrong, some other module had the symbol, so remember that:
|
||||
o.importIdx = idx
|
||||
@@ -578,7 +641,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
|
||||
proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
|
||||
assert o.currentScope == nil
|
||||
while o.importIdx < c.imports.len:
|
||||
result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
#while result != nil and result.id in o.marked:
|
||||
# result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
|
||||
if result != nil:
|
||||
@@ -602,12 +665,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
else:
|
||||
o.importIdx = 0
|
||||
if c.imports.len > 0:
|
||||
result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
result = nextOverloadIterImports(o, c, n)
|
||||
break
|
||||
elif o.importIdx < c.imports.len:
|
||||
result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config)
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
result = nextOverloadIterImports(o, c, n)
|
||||
else:
|
||||
@@ -615,7 +678,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
of oimSelfModule:
|
||||
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
|
||||
of oimOtherModule:
|
||||
result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config)
|
||||
result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config)
|
||||
of oimSymChoice:
|
||||
if o.symChoiceIndex < n.len:
|
||||
result = n[o.symChoiceIndex].sym
|
||||
@@ -654,7 +717,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
incl o.marked, result.id
|
||||
|
||||
elif o.importIdx < c.imports.len:
|
||||
result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config)
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
|
||||
#assert result.id notin o.marked
|
||||
#while result != nil and result.id in o.marked:
|
||||
# result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config)
|
||||
|
||||
@@ -71,7 +71,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextId(idgen),
|
||||
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)
|
||||
@@ -91,7 +91,7 @@ proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): P
|
||||
## freely, multiple times. This is frequently required and such a builtin would also be
|
||||
## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
|
||||
result = newNodeIT(nkStmtListExpr, value.info, value.typ)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextId(idgen),
|
||||
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)
|
||||
@@ -117,7 +117,7 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; o
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "_"), nextId(idgen), owner, value.info, owner.options)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
|
||||
@@ -135,7 +135,7 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; o
|
||||
proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
|
||||
var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextId(idgen), owner, n.info, owner.options)
|
||||
var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options)
|
||||
temp.typ = n[1].typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flags, sfGenSym)
|
||||
@@ -154,7 +154,7 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod
|
||||
result.add newFastAsgnStmt(n[2], tempAsNode)
|
||||
|
||||
proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
|
||||
result = newType(tyObject, nextId(idgen), owner)
|
||||
result = newType(tyObject, nextTypeId(idgen), owner)
|
||||
if final:
|
||||
rawAddSon(result, nil)
|
||||
incl result.flags, tfFinal
|
||||
@@ -162,7 +162,7 @@ proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo
|
||||
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
|
||||
result.n = newNodeI(nkRecList, info)
|
||||
let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
|
||||
nextId(idgen),
|
||||
nextSymId(idgen),
|
||||
owner, info, owner.options)
|
||||
incl s.flags, sfAnon
|
||||
s.typ = result
|
||||
@@ -225,7 +225,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) =
|
||||
# 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.
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
||||
nextId(idgen), s.owner, s.info, s.options)
|
||||
nextSymId(idgen), s.owner, s.info, s.options)
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
field.typ = t
|
||||
@@ -239,7 +239,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) =
|
||||
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
||||
result = lookupInRecord(obj.n, s.itemId)
|
||||
if result == nil:
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextId(idgen),
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen),
|
||||
s.owner, s.info, s.options)
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
@@ -326,7 +326,7 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
||||
proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
|
||||
result = newNodeI(nkAddr, n.info, 1)
|
||||
result[0] = n
|
||||
result.typ = newType(typeKind, nextId(idgen), n.typ.owner)
|
||||
result.typ = newType(typeKind, nextTypeId(idgen), n.typ.owner)
|
||||
result.typ.rawAddSon(n.typ)
|
||||
|
||||
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
||||
|
||||
@@ -9,71 +9,36 @@
|
||||
|
||||
## This module implements helpers for the macro cache.
|
||||
|
||||
import lineinfos, ast, modulegraphs, vmdef
|
||||
import lineinfos, ast, vmdef
|
||||
|
||||
proc append(c: PCtx; n: PNode) =
|
||||
c.vmstateDiff.add((c.module, n))
|
||||
|
||||
proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
|
||||
var recorded = newNodeI(nkCommentStmt, info)
|
||||
var recorded = newNodeI(nkReplayAction, info)
|
||||
recorded.add newStrNode("inc", info)
|
||||
recorded.add newStrNode(key, info)
|
||||
recorded.add newIntNode(nkIntLit, by)
|
||||
c.graph.recordStmt(c.graph, c.module, recorded)
|
||||
c.append(recorded)
|
||||
|
||||
proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
|
||||
var recorded = newNodeI(nkCommentStmt, info)
|
||||
var recorded = newNodeI(nkReplayAction, info)
|
||||
recorded.add newStrNode("put", info)
|
||||
recorded.add newStrNode(key, info)
|
||||
recorded.add newStrNode(k, info)
|
||||
recorded.add copyTree(val)
|
||||
c.graph.recordStmt(c.graph, c.module, recorded)
|
||||
c.append(recorded)
|
||||
|
||||
proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
|
||||
var recorded = newNodeI(nkCommentStmt, info)
|
||||
var recorded = newNodeI(nkReplayAction, info)
|
||||
recorded.add newStrNode("add", info)
|
||||
recorded.add newStrNode(key, info)
|
||||
recorded.add copyTree(val)
|
||||
c.graph.recordStmt(c.graph, c.module, recorded)
|
||||
c.append(recorded)
|
||||
|
||||
proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
|
||||
var recorded = newNodeI(nkCommentStmt, info)
|
||||
var recorded = newNodeI(nkReplayAction, info)
|
||||
recorded.add newStrNode("incl", info)
|
||||
recorded.add newStrNode(key, info)
|
||||
recorded.add copyTree(val)
|
||||
c.graph.recordStmt(c.graph, c.module, recorded)
|
||||
|
||||
when false:
|
||||
proc genCall3(g: ModuleGraph; m: TMagic; s: string; a, b, c: PNode): PNode =
|
||||
newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b, c))
|
||||
|
||||
proc genCall2(g: ModuleGraph; m: TMagic; s: string; a, b: PNode): PNode =
|
||||
newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b))
|
||||
|
||||
template nodeFrom(s: string): PNode =
|
||||
var res = newStrNode(s, info)
|
||||
res.typ = getSysType(g, info, tyString)
|
||||
res
|
||||
|
||||
template nodeFrom(i: BiggestInt): PNode =
|
||||
var res = newIntNode(i, info)
|
||||
res.typ = getSysType(g, info, tyInt)
|
||||
res
|
||||
|
||||
template nodeFrom(n: PNode): PNode = copyTree(n)
|
||||
|
||||
template record(call) =
|
||||
g.recordStmt(g, c.module, call)
|
||||
|
||||
proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
|
||||
let g = c.graph
|
||||
record genCall2(mNccInc, "inc", nodeFrom key, nodeFrom by)
|
||||
|
||||
proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
|
||||
let g = c.graph
|
||||
record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val)
|
||||
|
||||
proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
|
||||
let g = c.graph
|
||||
record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val)
|
||||
|
||||
proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
|
||||
let g = c.graph
|
||||
record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val)
|
||||
c.append(recorded)
|
||||
|
||||
@@ -17,36 +17,30 @@ export createMagic
|
||||
|
||||
proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
|
||||
|
||||
proc registerSysType*(g: ModuleGraph; t: PType) =
|
||||
if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t
|
||||
|
||||
proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
|
||||
result = newType(kind, nextId(g.idgen), g.systemModule)
|
||||
result = newType(kind, nextTypeId(g.idgen), g.systemModule)
|
||||
result.size = size
|
||||
result.align = size.int16
|
||||
|
||||
proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
|
||||
result = strTableGet(g.systemModule.tab, getIdent(g.cache, name))
|
||||
result = systemModuleSym(g, getIdent(g.cache, name))
|
||||
if result == nil:
|
||||
localError(g.config, info, "system module needs: " & name)
|
||||
result = newSym(skError, getIdent(g.cache, name), nextId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextId(g.idgen), g.systemModule)
|
||||
result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
|
||||
if result.kind == skAlias: result = result.owner
|
||||
|
||||
proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
|
||||
var ti: TIdentIter
|
||||
let id = getIdent(g.cache, name)
|
||||
var r = initIdentIter(ti, g.systemModule.tab, id)
|
||||
while r != nil:
|
||||
for r in systemModuleSyms(g, id):
|
||||
if r.magic == m:
|
||||
# prefer the tyInt variant:
|
||||
if r.typ[0] != nil and r.typ[0].kind == tyInt: return r
|
||||
result = r
|
||||
r = nextIdentIter(ti, g.systemModule.tab)
|
||||
if result != nil: return result
|
||||
localError(g.config, info, "system module needs: " & name)
|
||||
result = newSym(skError, id, nextId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextId(g.idgen), g.systemModule)
|
||||
result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
|
||||
|
||||
proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
|
||||
result = getSysSym(g, info, name).typ
|
||||
@@ -91,24 +85,6 @@ proc resetSysTypes*(g: ModuleGraph) =
|
||||
for i in low(g.sysTypes)..high(g.sysTypes):
|
||||
g.sysTypes[i] = nil
|
||||
|
||||
for i in low(g.intTypeCache)..high(g.intTypeCache):
|
||||
g.intTypeCache[i] = nil
|
||||
|
||||
proc getIntLitType*(g: ModuleGraph; literal: PNode): PType =
|
||||
# we cache some common integer literal types for performance:
|
||||
let value = literal.intVal
|
||||
if value >= low(g.intTypeCache) and value <= high(g.intTypeCache):
|
||||
result = g.intTypeCache[value.int]
|
||||
if result == nil:
|
||||
let ti = getSysType(g, literal.info, tyInt)
|
||||
result = copyType(ti, nextId(g.idgen), ti.owner)
|
||||
result.n = literal
|
||||
g.intTypeCache[value.int] = result
|
||||
else:
|
||||
let ti = getSysType(g, literal.info, tyInt)
|
||||
result = copyType(ti, nextId(g.idgen), ti.owner)
|
||||
result.n = literal
|
||||
|
||||
proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
|
||||
# for now we do not cache these:
|
||||
result = newSysType(g, tyFloat, size=8)
|
||||
@@ -116,50 +92,21 @@ proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
|
||||
|
||||
proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
|
||||
if t.n != nil and t.kind in {tyInt, tyFloat}:
|
||||
result = copyType(t, nextId(id), t.owner)
|
||||
result = copyType(t, nextTypeId(id), t.owner)
|
||||
result.n = nil
|
||||
else:
|
||||
result = t
|
||||
|
||||
proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
|
||||
when not defined(nimNoNilSeqs):
|
||||
if isNil(father.sons): father.sons = @[]
|
||||
let s = son.skipIntLit(id)
|
||||
father.sons.add(s)
|
||||
propagateToOwner(father, s)
|
||||
|
||||
proc setIntLitType*(g: ModuleGraph; result: PNode) =
|
||||
let i = result.intVal
|
||||
case g.config.target.intSize
|
||||
of 8: result.typ = getIntLitType(g, result)
|
||||
of 4:
|
||||
if i >= low(int32) and i <= high(int32):
|
||||
result.typ = getIntLitType(g, result)
|
||||
else:
|
||||
result.typ = getSysType(g, result.info, tyInt64)
|
||||
of 2:
|
||||
if i >= low(int16) and i <= high(int16):
|
||||
result.typ = getIntLitType(g, result)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ = getSysType(g, result.info, tyInt32)
|
||||
else:
|
||||
result.typ = getSysType(g, result.info, tyInt64)
|
||||
of 1:
|
||||
# 8 bit CPUs are insane ...
|
||||
if i >= low(int8) and i <= high(int8):
|
||||
result.typ = getIntLitType(g, result)
|
||||
elif i >= low(int16) and i <= high(int16):
|
||||
result.typ = getSysType(g, result.info, tyInt16)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ = getSysType(g, result.info, tyInt32)
|
||||
else:
|
||||
result.typ = getSysType(g, result.info, tyInt64)
|
||||
else:
|
||||
internalError(g.config, result.info, "invalid int size")
|
||||
|
||||
proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
|
||||
let ident = getIdent(g.cache, name)
|
||||
result = strTableGet(g.compilerprocs, ident)
|
||||
if result == nil:
|
||||
result = loadCompilerProc(g, name)
|
||||
|
||||
proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
|
||||
strTableAdd(g.compilerprocs, s)
|
||||
|
||||
@@ -19,7 +19,10 @@ import
|
||||
cgen, json, nversion,
|
||||
platform, nimconf, passaux, depends, vm,
|
||||
modules,
|
||||
modulegraphs, tables, rod, lineinfos, pathutils, vmprofiler
|
||||
modulegraphs, tables, lineinfos, pathutils, vmprofiler
|
||||
|
||||
import ic / cbackend
|
||||
from ic / ic import rodViewer
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import jsgen, docgen, docgen2
|
||||
@@ -71,14 +74,15 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
setOutFile(conf)
|
||||
extccomp.initVars(conf)
|
||||
semanticPasses(graph)
|
||||
registerPass(graph, cgenPass)
|
||||
if conf.symbolFiles == disabledSf:
|
||||
registerPass(graph, cgenPass)
|
||||
|
||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
||||
let proj = changeFileExt(conf.projectFull, "")
|
||||
if not changeDetectedViaJsonBuildInstructions(conf, proj):
|
||||
# nothing changed
|
||||
graph.config.notes = graph.config.mainPackageNotes
|
||||
return
|
||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
||||
let proj = changeFileExt(conf.projectFull, "")
|
||||
if not changeDetectedViaJsonBuildInstructions(conf, proj):
|
||||
# nothing changed
|
||||
graph.config.notes = graph.config.mainPackageNotes
|
||||
return
|
||||
|
||||
if not extccomp.ccHasSaneOverflow(conf):
|
||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||
@@ -86,8 +90,14 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
compileProject(graph)
|
||||
if graph.config.errorCounter > 0:
|
||||
return # issue #9933
|
||||
cgenWriteModules(graph.backend, conf)
|
||||
if conf.cmd != cmdTcc:
|
||||
if conf.symbolFiles == disabledSf:
|
||||
cgenWriteModules(graph.backend, conf)
|
||||
else:
|
||||
generateCode(graph)
|
||||
# graph.backend can be nil under IC when nothing changed at all:
|
||||
if graph.backend != nil:
|
||||
cgenWriteModules(graph.backend, conf)
|
||||
if conf.cmd != cmdTcc and graph.backend != nil:
|
||||
extccomp.callCCompiler(conf)
|
||||
# for now we do not support writing out a .json file with the build instructions when HCR is on
|
||||
if not conf.hcrOn:
|
||||
@@ -137,8 +147,9 @@ proc commandInteractive(graph: ModuleGraph) =
|
||||
else:
|
||||
var m = graph.makeStdinModule()
|
||||
incl(m.flags, sfMainModule)
|
||||
var idgen = IdGenerator(module: m.itemId.module, item: m.itemId.item)
|
||||
processModule(graph, m, idgen, llStreamOpenStdIn())
|
||||
var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
|
||||
processModule(graph, m, idgen, s)
|
||||
|
||||
proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||
var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
|
||||
@@ -157,6 +168,10 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||
else:
|
||||
rawMessage(config, errGenerated, "cannot open file: " & f.string)
|
||||
|
||||
proc commandView(graph: ModuleGraph) =
|
||||
let f = toAbsolute(mainCommandArg(graph.config), AbsoluteDir getCurrentDir()).addFileExt(RodExt)
|
||||
rodViewer(f, graph.config, graph.cache)
|
||||
|
||||
const
|
||||
PrintRopeCacheStats = false
|
||||
|
||||
@@ -164,7 +179,6 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
let cache = graph.cache
|
||||
|
||||
setupModuleCache(graph)
|
||||
# In "nim serve" scenario, each command must reset the registered passes
|
||||
clearPasses(graph)
|
||||
conf.lastCmdTime = epochTime()
|
||||
@@ -192,8 +206,6 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
# A better solution might be to fix system.nim
|
||||
undefSymbol(conf.symbols, "useNimRtl")
|
||||
of backendInvalid: doAssert false
|
||||
if conf.selectedGC in {gcArc, gcOrc} and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
|
||||
proc compileToBackend() =
|
||||
customizeForBackend(conf.backend)
|
||||
@@ -213,18 +225,17 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
defineSymbol(conf.symbols, "nimdoc")
|
||||
body
|
||||
|
||||
block: ## command prepass
|
||||
if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache}
|
||||
if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC)
|
||||
if conf.outDir.isEmpty:
|
||||
# doc like commands can generate a lot of files (especially with --project)
|
||||
# so by default should not end up in $PWD nor in $projectPath.
|
||||
conf.outDir = block:
|
||||
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
|
||||
else: conf.projectPath
|
||||
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
|
||||
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir
|
||||
ret
|
||||
## command prepass
|
||||
if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache}
|
||||
if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC)
|
||||
if conf.outDir.isEmpty:
|
||||
# doc like commands can generate a lot of files (especially with --project)
|
||||
# so by default should not end up in $PWD nor in $projectPath.
|
||||
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
|
||||
else: conf.projectPath
|
||||
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
|
||||
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir
|
||||
conf.outDir = ret
|
||||
|
||||
## process all commands
|
||||
case conf.cmd
|
||||
@@ -250,6 +261,10 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
|
||||
commandBuildIndex(conf, $conf.outDir)
|
||||
of cmdRst2html:
|
||||
# XXX: why are warnings disabled by default for rst2html and rst2tex?
|
||||
for warn in [warnUnknownSubstitutionX, warnLanguageXNotSupported,
|
||||
warnFieldXNotSupported, warnRstStyle]:
|
||||
conf.setNoteDefaults(warn, true)
|
||||
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218
|
||||
when defined(leanCompiler):
|
||||
quit "compiler wasn't built with documentation generator"
|
||||
@@ -257,6 +272,10 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
loadConfigs(DocConfig, cache, conf, graph.idgen)
|
||||
commandRst2Html(cache, conf)
|
||||
of cmdRst2tex:
|
||||
for warn in [warnRedefinitionOfLabel, warnUnknownSubstitutionX,
|
||||
warnLanguageXNotSupported,
|
||||
warnFieldXNotSupported, warnRstStyle]:
|
||||
conf.setNoteDefaults(warn, true)
|
||||
when defined(leanCompiler):
|
||||
quit "compiler wasn't built with documentation generator"
|
||||
else:
|
||||
@@ -314,10 +333,10 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdParse:
|
||||
wantMainModule(conf)
|
||||
discard parseFile(conf.projectMainIdx, cache, conf)
|
||||
of cmdScan:
|
||||
of cmdRod:
|
||||
wantMainModule(conf)
|
||||
commandScan(cache, conf)
|
||||
msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
|
||||
commandView(graph)
|
||||
#msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
|
||||
of cmdInteractive: commandInteractive(graph)
|
||||
of cmdNimscript:
|
||||
if conf.projectIsCmd or conf.projectIsStdin: discard
|
||||
|
||||
@@ -9,44 +9,66 @@
|
||||
|
||||
## This module implements the module graph data structure. The module graph
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a Sqlite database.
|
||||
##
|
||||
## The caching of modules is critical for 'nimsuggest' and is tricky to get
|
||||
## right. If module E is being edited, we need autocompletion (and type
|
||||
## checking) for E but we don't want to recompile depending
|
||||
## modules right away for faster turnaround times. Instead we mark the module's
|
||||
## dependencies as 'dirty'. Let D be a dependency of E. If D is dirty, we
|
||||
## need to recompile it and all of its dependencies that are marked as 'dirty'.
|
||||
## 'nimsuggest sug' actually is invoked for the file being edited so we know
|
||||
## its content changed and there is no need to compute any checksums.
|
||||
## Instead of a recursive algorithm, we use an iterative algorithm:
|
||||
##
|
||||
## - If a module gets recompiled, its dependencies need to be updated.
|
||||
## - Its dependent module stays the same.
|
||||
##
|
||||
## or stored in a rod-file.
|
||||
|
||||
import ast, intsets, tables, options, lineinfos, hashes, idents,
|
||||
incremental, btrees, md5
|
||||
import ast, astalgo, intsets, tables, options, lineinfos, hashes, idents,
|
||||
btrees, md5, ropes, msgs
|
||||
|
||||
# import ic / packed_ast
|
||||
import ic / [packed_ast, ic]
|
||||
|
||||
type
|
||||
SigHash* = distinct MD5Digest
|
||||
|
||||
LazySym* = object
|
||||
id*: FullId
|
||||
sym*: PSym
|
||||
|
||||
Iface* = object ## data we don't want to store directly in the
|
||||
## ast.PSym type for s.kind == skModule
|
||||
module*: PSym ## module this "Iface" belongs to
|
||||
converters*: seq[PSym]
|
||||
patterns*: seq[PSym]
|
||||
pureEnums*: seq[PSym]
|
||||
converters*: seq[LazySym]
|
||||
patterns*: seq[LazySym]
|
||||
pureEnums*: seq[LazySym]
|
||||
interf: TStrTable
|
||||
uniqueName*: Rope
|
||||
|
||||
Operators* = object
|
||||
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
|
||||
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
|
||||
|
||||
FullId* = object
|
||||
module*: int
|
||||
packed*: PackedItemId
|
||||
|
||||
LazyType* = object
|
||||
id*: FullId
|
||||
typ*: PType
|
||||
|
||||
LazyInstantiation* = object
|
||||
module*: int
|
||||
sym*: FullId
|
||||
concreteTypes*: seq[FullId]
|
||||
inst*: PInstantiation
|
||||
|
||||
ModuleGraph* = ref object
|
||||
ifaces*: seq[Iface] ## indexed by int32 fileIdx
|
||||
packed*: PackedModuleGraph
|
||||
encoders*: seq[PackedEncoder]
|
||||
|
||||
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
|
||||
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
|
||||
attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
|
||||
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
||||
enumToStringProcs*: Table[ItemId, LazySym]
|
||||
|
||||
startupPackedConfig*: PackedConfig
|
||||
packageSyms*: TStrTable
|
||||
modulesPerPackage*: Table[ItemId, TStrTable]
|
||||
deps*: IntSet # the dependency graph or potentially its transitive closure.
|
||||
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
|
||||
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
||||
invalidTransitiveClosure: bool
|
||||
systemModuleComplete*: bool
|
||||
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
|
||||
# first module that included it
|
||||
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
|
||||
@@ -64,18 +86,15 @@ type
|
||||
sysTypes*: array[TTypeKind, PType]
|
||||
compilerprocs*: TStrTable
|
||||
exposed*: TStrTable
|
||||
intTypeCache*: array[-5..64, PType]
|
||||
opContains*, opNot*: PSym
|
||||
packageTypes*: TStrTable
|
||||
emptyNode*: PNode
|
||||
incr*: IncrementalCtx
|
||||
canonTypes*: Table[SigHash, PType]
|
||||
symBodyHashes*: Table[int, SigHash] # symId to digest mapping
|
||||
importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.}
|
||||
includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.}
|
||||
recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.}
|
||||
cacheSeqs*: Table[string, PNode] # state that is shared to support the 'macrocache' API
|
||||
cacheCounters*: Table[string, BiggestInt]
|
||||
cacheTables*: Table[string, BTree[string, PNode]]
|
||||
cacheSeqs*: Table[string, PNode] # state that is shared to support the 'macrocache' API; IC: implemented
|
||||
cacheCounters*: Table[string, BiggestInt] # IC: implemented
|
||||
cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
|
||||
passes*: seq[TPass]
|
||||
onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
@@ -84,6 +103,7 @@ type
|
||||
strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
|
||||
compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
|
||||
idgen*: IdGenerator
|
||||
operators*: Operators
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
@@ -98,6 +118,14 @@ type
|
||||
close: TPassClose,
|
||||
isFrontend: bool]
|
||||
|
||||
proc resetForBackend*(g: ModuleGraph) =
|
||||
initStrTable(g.compilerprocs)
|
||||
g.typeInstCache.clear()
|
||||
g.procInstCache.clear()
|
||||
for a in mitems(g.attachedOps):
|
||||
a.clear()
|
||||
g.methodsPerType.clear()
|
||||
g.enumToStringProcs.clear()
|
||||
|
||||
const
|
||||
cb64 = [
|
||||
@@ -133,6 +161,172 @@ proc toBase64a(s: cstring, len: int): string =
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[(a and 3) shl 4]
|
||||
|
||||
template semtab*(m: PSym; g: ModuleGraph): TStrTable =
|
||||
g.ifaces[m.position].interf
|
||||
|
||||
proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
|
||||
result = module < g.packed.len and g.packed[module].status == loaded
|
||||
|
||||
proc isCachedModule(g: ModuleGraph; m: PSym): bool {.inline.} =
|
||||
isCachedModule(g, m.position)
|
||||
|
||||
proc simulateCachedModule*(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
|
||||
when false:
|
||||
echo "simulating ", moduleSym.name.s, " ", moduleSym.position
|
||||
simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
|
||||
|
||||
proc initEncoder*(g: ModuleGraph; module: PSym) =
|
||||
let id = module.position
|
||||
if id >= g.encoders.len:
|
||||
setLen g.encoders, id+1
|
||||
ic.initEncoder(g.encoders[id],
|
||||
g.packed[id].fromDisk, module, g.config, g.startupPackedConfig)
|
||||
|
||||
type
|
||||
ModuleIter* = object
|
||||
fromRod: bool
|
||||
modIndex: int
|
||||
ti: TIdentIter
|
||||
rodIt: RodIter
|
||||
|
||||
proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym =
|
||||
assert m.kind == skModule
|
||||
mi.modIndex = m.position
|
||||
mi.fromRod = isCachedModule(g, mi.modIndex)
|
||||
if mi.fromRod:
|
||||
result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name)
|
||||
else:
|
||||
result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interf, name)
|
||||
|
||||
proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
|
||||
if mi.fromRod:
|
||||
result = nextRodIter(mi.rodIt, g.packed)
|
||||
else:
|
||||
result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interf)
|
||||
|
||||
iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
|
||||
if isCachedModule(g, m):
|
||||
var rodIt: RodIter
|
||||
var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position)
|
||||
while r != nil:
|
||||
yield r
|
||||
r = nextRodIter(rodIt, g.packed)
|
||||
else:
|
||||
for s in g.ifaces[m.position].interf.data:
|
||||
if s != nil:
|
||||
yield s
|
||||
|
||||
proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
|
||||
if isCachedModule(g, m):
|
||||
result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name)
|
||||
else:
|
||||
result = strTableGet(g.ifaces[m.position].interf, name)
|
||||
|
||||
proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
|
||||
result = someSym(g, g.systemModule, name)
|
||||
|
||||
iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
|
||||
var mi: ModuleIter
|
||||
var r = initModuleIter(mi, g, g.systemModule, name)
|
||||
while r != nil:
|
||||
yield r
|
||||
r = nextModuleIter(mi, g)
|
||||
|
||||
proc resolveType(g: ModuleGraph; t: var LazyType): PType =
|
||||
result = t.typ
|
||||
if result == nil and isCachedModule(g, t.id.module):
|
||||
result = loadTypeFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
|
||||
t.typ = result
|
||||
assert result != nil
|
||||
|
||||
proc resolveSym(g: ModuleGraph; t: var LazySym): PSym =
|
||||
result = t.sym
|
||||
if result == nil and isCachedModule(g, t.id.module):
|
||||
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
|
||||
t.sym = result
|
||||
assert result != nil
|
||||
|
||||
proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
|
||||
result = t.inst
|
||||
if result == nil and isCachedModule(g, t.module):
|
||||
result = PInstantiation(sym: loadSymFromId(g.config, g.cache, g.packed, t.sym.module, t.sym.packed))
|
||||
result.concreteTypes = newSeq[PType](t.concreteTypes.len)
|
||||
for i in 0..high(result.concreteTypes):
|
||||
result.concreteTypes[i] = loadTypeFromId(g.config, g.cache, g.packed,
|
||||
t.concreteTypes[i].module, t.concreteTypes[i].packed)
|
||||
t.inst = result
|
||||
assert result != nil
|
||||
|
||||
iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
|
||||
if g.typeInstCache.contains(s.itemId):
|
||||
let x = addr(g.typeInstCache[s.itemId])
|
||||
for t in mitems(x[]):
|
||||
yield resolveType(g, t)
|
||||
|
||||
iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
|
||||
if g.procInstCache.contains(s.itemId):
|
||||
let x = addr(g.procInstCache[s.itemId])
|
||||
for t in mitems(x[]):
|
||||
yield resolveInst(g, t)
|
||||
|
||||
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
|
||||
## returns the requested attached operation for type `t`. Can return nil
|
||||
## if no such operation exists.
|
||||
result = g.attachedOps[op].getOrDefault(t.itemId)
|
||||
|
||||
proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||
## we also need to record this to the packed module.
|
||||
g.attachedOps[op][t.itemId] = value
|
||||
|
||||
proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||
## we also need to record this to the packed module.
|
||||
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) =
|
||||
if g.config.symbolFiles != disabledSf:
|
||||
assert module < g.encoders.len
|
||||
assert isActive(g.encoders[module])
|
||||
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
|
||||
|
||||
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
|
||||
result = resolveSym(g, g.enumToStringProcs[t.itemId])
|
||||
assert result != nil
|
||||
|
||||
proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
|
||||
g.enumToStringProcs[t.itemId] = LazySym(sym: value)
|
||||
|
||||
iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
|
||||
if g.methodsPerType.contains(t.itemId):
|
||||
for it in mitems g.methodsPerType[t.itemId]:
|
||||
yield (it[0], resolveSym(g, it[1]))
|
||||
|
||||
proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
|
||||
g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
|
||||
|
||||
proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedAsgn)
|
||||
result = op != nil and sfError in op.flags
|
||||
|
||||
proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
|
||||
for k in low(TTypeAttachedOp)..high(TTypeAttachedOp):
|
||||
let op = getAttachedOp(g, src, k)
|
||||
if op != nil:
|
||||
setAttachedOp(g, module, dest, k, op)
|
||||
|
||||
proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
|
||||
if g.config.symbolFiles == disabledSf: return nil
|
||||
|
||||
# slow, linear search, but the results are cached:
|
||||
for module in 0..high(g.packed):
|
||||
#if isCachedModule(g, module):
|
||||
let x = searchForCompilerproc(g.packed[module], name)
|
||||
if x >= 0:
|
||||
result = loadSymFromId(g.config, g.cache, g.packed, module, toPackedItemId(x))
|
||||
if result != nil:
|
||||
strTableAdd(g.compilerprocs, result)
|
||||
return result
|
||||
|
||||
proc `$`*(u: SigHash): string =
|
||||
toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
|
||||
|
||||
@@ -175,22 +369,52 @@ else:
|
||||
proc stopCompile*(g: ModuleGraph): bool {.inline.} =
|
||||
result = g.doStopCompile != nil and g.doStopCompile()
|
||||
|
||||
proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, name), nextId(g.idgen), nil, unknownLineInfo, {})
|
||||
proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, name), nextSymId(idgen), nil, unknownLineInfo, {})
|
||||
result.magic = m
|
||||
result.flags = {sfNeverRaises}
|
||||
|
||||
proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
|
||||
result = createMagic(g, g.idgen, name, m)
|
||||
|
||||
proc registerModule*(g: ModuleGraph; m: PSym) =
|
||||
assert m != nil
|
||||
assert m.kind == skModule
|
||||
|
||||
if m.position >= g.ifaces.len:
|
||||
setLen(g.ifaces, m.position + 1)
|
||||
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[])
|
||||
|
||||
if m.position >= g.packed.len:
|
||||
setLen(g.packed, m.position + 1)
|
||||
|
||||
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
|
||||
uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
|
||||
initStrTable(g.ifaces[m.position].interf)
|
||||
|
||||
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
|
||||
registerModule(g, g.packed[int m].module)
|
||||
|
||||
proc initOperators(g: ModuleGraph): Operators =
|
||||
# These are safe for IC.
|
||||
result.opLe = createMagic(g, "<=", mLeI)
|
||||
result.opLt = createMagic(g, "<", mLtI)
|
||||
result.opAnd = createMagic(g, "and", mAnd)
|
||||
result.opOr = createMagic(g, "or", mOr)
|
||||
result.opIsNil = createMagic(g, "isnil", mIsNil)
|
||||
result.opEq = createMagic(g, "==", mEqI)
|
||||
result.opAdd = createMagic(g, "+", mAddI)
|
||||
result.opSub = createMagic(g, "-", mSubI)
|
||||
result.opMul = createMagic(g, "*", mMulI)
|
||||
result.opDiv = createMagic(g, "div", mDivI)
|
||||
result.opLen = createMagic(g, "len", mLengthSeq)
|
||||
result.opNot = createMagic(g, "not", mNot)
|
||||
result.opContains = createMagic(g, "contains", mInSet)
|
||||
|
||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result = ModuleGraph()
|
||||
result.idgen = IdGenerator(module: -1'i32, item: 0'i32)
|
||||
# A module ID of -1 means that the symbol is not attached to a module at all,
|
||||
# but to the module graph:
|
||||
result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
|
||||
initStrTable(result.packageSyms)
|
||||
result.deps = initIntSet()
|
||||
result.importDeps = initTable[FileIndex, seq[FileIndex]]()
|
||||
@@ -203,17 +427,14 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result.methods = @[]
|
||||
initStrTable(result.compilerprocs)
|
||||
initStrTable(result.exposed)
|
||||
result.opNot = createMagic(result, "not", mNot)
|
||||
result.opContains = createMagic(result, "contains", mInSet)
|
||||
initStrTable(result.packageTypes)
|
||||
result.emptyNode = newNode(nkEmpty)
|
||||
init(result.incr)
|
||||
result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} =
|
||||
discard
|
||||
result.cacheSeqs = initTable[string, PNode]()
|
||||
result.cacheCounters = initTable[string, BiggestInt]()
|
||||
result.cacheTables = initTable[string, BTree[string, PNode]]()
|
||||
result.canonTypes = initTable[SigHash, PType]()
|
||||
result.symBodyHashes = initTable[int, SigHash]()
|
||||
result.operators = initOperators(result)
|
||||
|
||||
proc resetAllModules*(g: ModuleGraph) =
|
||||
initStrTable(g.packageSyms)
|
||||
@@ -228,14 +449,16 @@ proc resetAllModules*(g: ModuleGraph) =
|
||||
initStrTable(g.exposed)
|
||||
|
||||
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len:
|
||||
result = g.ifaces[fileIdx.int32].module
|
||||
if fileIdx.int32 >= 0:
|
||||
if isCachedModule(g, fileIdx.int32):
|
||||
result = g.packed[fileIdx.int32].module
|
||||
elif fileIdx.int32 < g.ifaces.len:
|
||||
result = g.ifaces[fileIdx.int32].module
|
||||
|
||||
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
|
||||
|
||||
proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
|
||||
assert m.position == m.info.fileIndex.int32
|
||||
addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
|
||||
if g.suggestMode:
|
||||
g.deps.incl m.position.dependsOn(dep.int)
|
||||
# we compute the transitive closure later when querying the graph lazily.
|
||||
@@ -243,7 +466,6 @@ proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
|
||||
#invalidTransitiveClosure = true
|
||||
|
||||
proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
|
||||
addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
|
||||
discard hasKeyOrPut(g.inclToMod, includeFile, module)
|
||||
|
||||
proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
|
||||
@@ -284,3 +506,19 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||
|
||||
proc isDirty*(g: ModuleGraph; m: PSym): bool =
|
||||
result = g.suggestMode and sfDirty in m.flags
|
||||
|
||||
proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
|
||||
result = s.ast[bodyPos]
|
||||
if result == nil and g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
|
||||
result = loadProcBody(g.config, g.cache, g.packed, s)
|
||||
s.ast[bodyPos] = result
|
||||
assert result != nil
|
||||
|
||||
proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
cachedModules: var seq[FileIndex]): PSym =
|
||||
## Returns 'nil' if the module needs to be recompiled.
|
||||
if g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
|
||||
result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx, cachedModules)
|
||||
|
||||
proc configComplete*(g: ModuleGraph) =
|
||||
rememberStartupConfig(g.startupPackedConfig, g.config)
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
|
||||
import
|
||||
ast, astalgo, magicsys, msgs, options,
|
||||
idents, lexer, passes, syntaxes, llstream, modulegraphs, rod,
|
||||
idents, lexer, passes, syntaxes, llstream, modulegraphs,
|
||||
lineinfos, pathutils, tables
|
||||
|
||||
import ic / replayer
|
||||
|
||||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||
magicsys.resetSysTypes(g)
|
||||
|
||||
@@ -31,13 +33,14 @@ proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
pck = getPackageName(graph.config, filename.string)
|
||||
pck2 = if pck.len > 0: pck else: "unknown"
|
||||
pack = getIdent(graph.cache, pck2)
|
||||
var packSym = graph.packageSyms.strTableGet(pack)
|
||||
if packSym == nil:
|
||||
packSym = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info)
|
||||
initStrTable(packSym.tab)
|
||||
graph.packageSyms.strTableAdd(packSym)
|
||||
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 existing = strTableGet(packSym.tab, name)
|
||||
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:
|
||||
@@ -48,26 +51,26 @@ proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
# 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 `packSym`'s owner be the original `packSym`
|
||||
packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info)
|
||||
initStrTable(packSym.tab)
|
||||
graph.packageSyms.strTableAdd(packSym)
|
||||
result = packSym
|
||||
# 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)
|
||||
result.owner = packSym
|
||||
result.position = int fileIdx
|
||||
|
||||
graph.registerModule(result)
|
||||
|
||||
initStrTable(result.tab)
|
||||
#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
|
||||
strTableAdd(packSym.tab, result)
|
||||
|
||||
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)
|
||||
@@ -79,6 +82,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
if not isNimIdentifier(result.name.s):
|
||||
rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
|
||||
partialInitModule(result, graph, fileIdx, filename)
|
||||
graph.registerModule(result)
|
||||
|
||||
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
|
||||
var flags = flags
|
||||
@@ -92,19 +96,26 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P
|
||||
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)
|
||||
result = loadModuleSym(graph, fileIdx, filename)
|
||||
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)
|
||||
processModuleAux()
|
||||
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:
|
||||
initStrTable(result.tab)
|
||||
initStrTable(result.semtab(graph))
|
||||
result.ast = nil
|
||||
processModuleAux()
|
||||
graph.markClientsDirty(fileIdx)
|
||||
@@ -150,6 +161,8 @@ 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
|
||||
|
||||
@@ -19,9 +19,11 @@ template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
|
||||
template toStdOrrKind(stdOrr): untyped =
|
||||
if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
|
||||
|
||||
template flushDot(conf, stdOrr) =
|
||||
proc flushDot*(conf: ConfigRef) =
|
||||
## safe to call multiple times
|
||||
let stdOrrKind = stdOrr.toStdOrrKind()
|
||||
# xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
|
||||
let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr
|
||||
let stdOrrKind = toStdOrrKind(stdOrr)
|
||||
if stdOrrKind in conf.lastMsgWasDot:
|
||||
conf.lastMsgWasDot.excl stdOrrKind
|
||||
write(stdOrr, "\n")
|
||||
@@ -113,6 +115,7 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
|
||||
else:
|
||||
isKnownFile = false
|
||||
result = conf.m.fileInfos.len.FileIndex
|
||||
#echo "ID ", result.int, " ", canon2
|
||||
conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
|
||||
else: relativeTo(canon, conf.projectPath)))
|
||||
conf.m.filenameToIndexTbl[canon2] = result
|
||||
@@ -311,12 +314,12 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
|
||||
conf.writelnHook(s)
|
||||
elif optStdout in conf.globalOptions or msgStdout in flags:
|
||||
if eStdOut in conf.m.errorOutputs:
|
||||
flushDot(conf, stdout)
|
||||
flushDot(conf)
|
||||
writeLine(stdout, s)
|
||||
flushFile(stdout)
|
||||
else:
|
||||
if eStdErr in conf.m.errorOutputs:
|
||||
flushDot(conf, stderr)
|
||||
flushDot(conf)
|
||||
writeLine(stderr, s)
|
||||
# On Windows stderr is fully-buffered when piped, regardless of C std.
|
||||
when defined(windows):
|
||||
@@ -368,11 +371,11 @@ template styledMsgWriteln*(args: varargs[typed]) =
|
||||
callIgnoringStyle(callWritelnHook, nil, args)
|
||||
elif optStdout in conf.globalOptions:
|
||||
if eStdOut in conf.m.errorOutputs:
|
||||
flushDot(conf, stdout)
|
||||
flushDot(conf)
|
||||
callIgnoringStyle(writeLine, stdout, args)
|
||||
flushFile(stdout)
|
||||
elif eStdErr in conf.m.errorOutputs:
|
||||
flushDot(conf, stderr)
|
||||
flushDot(conf)
|
||||
if optUseColors in conf.globalOptions:
|
||||
callStyledWriteLineStderr(args)
|
||||
else:
|
||||
@@ -411,7 +414,7 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string)
|
||||
if conf.cmd == cmdIdeTools: log(s)
|
||||
quit(conf, msg)
|
||||
if msg >= errMin and msg <= errMax or
|
||||
(msg in warnMin..warnMax and msg in conf.warningAsErrors):
|
||||
(msg in warnMin..hintMax and msg in conf.warningAsErrors):
|
||||
inc(conf.errorCounter)
|
||||
conf.exitcode = 1'i8
|
||||
if conf.errorCounter >= conf.errorMax:
|
||||
@@ -458,7 +461,7 @@ proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
|
||||
if result == 0:
|
||||
try:
|
||||
for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
|
||||
addSourceLine conf, fileIdx, line.string
|
||||
addSourceLine conf, fileIdx, line
|
||||
except IOError:
|
||||
discard
|
||||
result = conf.m.fileInfos[fileIdx.int32].lines.len
|
||||
@@ -520,7 +523,11 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
of hintMin..hintMax:
|
||||
sev = Severity.Hint
|
||||
ignoreMsg = not conf.hasHint(msg)
|
||||
title = HintTitle
|
||||
if msg in conf.warningAsErrors:
|
||||
ignoreMsg = false
|
||||
title = ErrorTitle
|
||||
else:
|
||||
title = HintTitle
|
||||
color = HintColor
|
||||
inc(conf.hintCounter)
|
||||
|
||||
@@ -584,6 +591,9 @@ template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: o
|
||||
template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||
liMessage(conf, info, msg, arg, doNothing, instLoc())
|
||||
|
||||
proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "") {.inline.} =
|
||||
message(conf, info, warnDeprecated, msg)
|
||||
|
||||
proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
|
||||
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
|
||||
writeContext(conf, info)
|
||||
@@ -621,3 +631,28 @@ template listMsg(title, r) =
|
||||
|
||||
proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
|
||||
proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
|
||||
|
||||
proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
|
||||
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
|
||||
## so that it is useful as a C identifier snippet.
|
||||
let path = AbsoluteFile toFullPath(conf, fid)
|
||||
let rel =
|
||||
if path.string.startsWith(conf.libpath.string):
|
||||
relativeTo(path, conf.libpath).string
|
||||
else:
|
||||
relativeTo(path, conf.projectPath).string
|
||||
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
|
||||
result = newStringOfCap(trunc)
|
||||
for i in 0..<trunc:
|
||||
let c = rel[i]
|
||||
case c
|
||||
of 'a'..'z':
|
||||
result.add c
|
||||
of {os.DirSep, os.AltSep}:
|
||||
result.add 'Z' # because it looks a bit like '/'
|
||||
of '.':
|
||||
result.add 'O' # a circle
|
||||
else:
|
||||
# We mangle upper letters and digits too so that there cannot
|
||||
# be clashes with our special meanings of 'Z' and 'O'
|
||||
result.addInt ord(c)
|
||||
|
||||
1382
compiler/nilcheck.nim
Normal file
1382
compiler/nilcheck.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,8 @@ hint[XDeclaredButNotUsed]:off
|
||||
|
||||
define:booting
|
||||
define:nimcore
|
||||
#define:nimIncremental
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
#define:staticSqlite
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@if windows:
|
||||
cincludes: "$lib/wrappers/libffi/common"
|
||||
|
||||
@@ -95,9 +95,15 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
var cmdPrefix = ""
|
||||
case conf.backend
|
||||
of backendC, backendCpp, backendObjc: discard
|
||||
of backendJs: cmdPrefix = findNodeJs() & " "
|
||||
of backendJs:
|
||||
# D20210217T215950:here this flag is needed for node < v15.0.0, otherwise
|
||||
# tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
|
||||
cmdPrefix = findNodeJs() & " --unhandled-rejections=strict "
|
||||
else: doAssert false, $conf.backend
|
||||
# No space before command otherwise on windows you'd get a cryptic:
|
||||
# `The parameter is incorrect`
|
||||
execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments)
|
||||
# execExternalProgram(conf, cmdPrefix & ' ' & output.quoteShell & ' ' & conf.arguments)
|
||||
of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing)
|
||||
if conf.arguments.len > 0:
|
||||
# reserved for future use
|
||||
@@ -110,7 +116,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
||||
when declared(GC_setMaxPause):
|
||||
GC_setMaxPause 2_000
|
||||
|
||||
when compileOption("gc", "v2") or compileOption("gc", "refc"):
|
||||
when compileOption("gc", "refc"):
|
||||
# the new correct mark&sweet collector is too slow :-/
|
||||
GC_disableMarkAndSweep()
|
||||
|
||||
|
||||
@@ -281,6 +281,8 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
|
||||
|
||||
if optSkipProjConfigFile notin conf.globalOptions:
|
||||
readConfigFile(pd / cfg)
|
||||
if cfg == DefaultConfig:
|
||||
runNimScriptIfExists(pd / DefaultConfigNims)
|
||||
|
||||
if conf.projectName.len != 0:
|
||||
# new project wide config file:
|
||||
@@ -289,8 +291,6 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
|
||||
projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
|
||||
readConfigFile(projectConfig)
|
||||
|
||||
if cfg == DefaultConfig:
|
||||
runNimScriptIfExists(pd / DefaultConfigNims)
|
||||
|
||||
let scriptFile = conf.projectFull.changeFileExt("nims")
|
||||
let scriptIsProj = scriptFile == conf.projectFull
|
||||
|
||||
@@ -12,7 +12,7 @@ import
|
||||
ast, astalgo, modules, passes, condsyms,
|
||||
options, sem, llstream, lineinfos, vm,
|
||||
vmdef, modulegraphs, idents, os, pathutils,
|
||||
passaux, scriptconfig
|
||||
passaux, scriptconfig, std/compilesettings
|
||||
|
||||
type
|
||||
Interpreter* = ref object ## Use Nim as an interpreter with this object
|
||||
@@ -24,11 +24,8 @@ type
|
||||
iterator exportedSymbols*(i: Interpreter): PSym =
|
||||
assert i != nil
|
||||
assert i.mainModule != nil, "no main module selected"
|
||||
var it: TTabIter
|
||||
var s = initTabIter(it, i.mainModule.tab)
|
||||
while s != nil:
|
||||
for s in modulegraphs.allSyms(i.graph, i.mainModule):
|
||||
yield s
|
||||
s = nextIter(it, i.mainModule.tab)
|
||||
|
||||
proc selectUniqueSymbol*(i: Interpreter; name: string;
|
||||
symKinds: set[TSymKind] = {skLet, skVar}): PSym =
|
||||
@@ -37,14 +34,14 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
|
||||
assert i != nil
|
||||
assert i.mainModule != nil, "no main module selected"
|
||||
let n = getIdent(i.graph.cache, name)
|
||||
var it: TIdentIter
|
||||
var s = initIdentIter(it, i.mainModule.tab, n)
|
||||
var it: ModuleIter
|
||||
var s = initModuleIter(it, i.graph, i.mainModule, n)
|
||||
result = nil
|
||||
while s != nil:
|
||||
if s.kind in symKinds:
|
||||
if result == nil: result = s
|
||||
else: return nil # ambiguous
|
||||
s = nextIdentIter(it, i.mainModule.tab)
|
||||
s = nextModuleIter(it, i.graph)
|
||||
|
||||
proc selectRoutine*(i: Interpreter; name: string): PSym =
|
||||
## Selects a declared routine (proc/func/etc) from the main module.
|
||||
@@ -70,7 +67,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
|
||||
## This can also be used to *reload* the script.
|
||||
assert i != nil
|
||||
assert i.mainModule != nil, "no main module selected"
|
||||
initStrTable(i.mainModule.tab)
|
||||
initStrTable(i.mainModule.semtab(i.graph))
|
||||
i.mainModule.ast = nil
|
||||
|
||||
let s = if scriptStream != nil: scriptStream
|
||||
@@ -95,10 +92,9 @@ proc findNimStdLib*(): string =
|
||||
return ""
|
||||
|
||||
proc findNimStdLibCompileTime*(): string =
|
||||
## Same as ``findNimStdLib`` but uses source files used at compile time,
|
||||
## Same as `findNimStdLib` but uses source files used at compile time,
|
||||
## and asserts on error.
|
||||
const exe = getCurrentCompilerExe()
|
||||
result = exe.splitFile.dir.parentDir / "lib"
|
||||
result = querySetting(libPath)
|
||||
doAssert fileExists(result / "system.nim"), "result:" & result
|
||||
|
||||
proc createInterpreter*(scriptName: string;
|
||||
|
||||
@@ -103,7 +103,7 @@ proc handleCmdLine(config: ConfigRef) =
|
||||
processCmdLine(passCmd2, "", config)
|
||||
mainCommand()
|
||||
|
||||
when compileOption("gc", "v2") or compileOption("gc", "refc"):
|
||||
when compileOption("gc", "refc"):
|
||||
GC_disableMarkAndSweep()
|
||||
|
||||
condsyms.initDefines()
|
||||
|
||||
@@ -3,7 +3,8 @@ Represents absolute paths, but using a symbolic variables (eg $nimr) which can b
|
||||
resolved at runtime; this avoids hardcoding at compile time absolute paths so
|
||||
that the project root can be relocated.
|
||||
|
||||
xxx consider some refactoring with $nim/testament/lib/stdtest/specialpaths.nim;
|
||||
xxx factor pending https://github.com/timotheecour/Nim/issues/616, see also
|
||||
$nim/testament/lib/stdtest/specialpaths.nim
|
||||
specialpaths is simpler because it doesn't need variables to be relocatable at
|
||||
runtime (eg for use in testament)
|
||||
|
||||
|
||||
@@ -150,7 +150,8 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
|
||||
of 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:
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
discard "do not follow the construct"
|
||||
|
||||
of nkAsgn, nkFastAsgn:
|
||||
@@ -249,7 +250,8 @@ proc opt(c: Con; n, parent: PNode; parentPos: int) =
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr:
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr,
|
||||
nkMixinStmt, nkBindStmt:
|
||||
parent[parentPos] = n
|
||||
|
||||
else:
|
||||
|
||||
@@ -19,7 +19,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2020"
|
||||
copyrightYear* = "2021"
|
||||
|
||||
type # please make sure we have under 32 options
|
||||
# (improves code efficiency a lot!)
|
||||
@@ -39,7 +39,6 @@ type # please make sure we have under 32 options
|
||||
# evaluation
|
||||
optTrMacros, # en/disable pattern matching
|
||||
optMemTracker,
|
||||
optNilSeqs,
|
||||
optSinkInference # 'sink T' inference
|
||||
optCursorInference
|
||||
|
||||
@@ -68,7 +67,6 @@ type # please make sure we have under 32 options
|
||||
optThreads, # support for multi-threading
|
||||
optStdout, # output to stdout
|
||||
optThreadAnalysis, # thread analysis pass
|
||||
optTaintMode, # taint mode turned on
|
||||
optTlsEmulation, # thread var emulation turned on
|
||||
optGenIndex # generate index file for documentation;
|
||||
optEmbedOrigSrc # embed the original source in the generated code
|
||||
@@ -103,8 +101,23 @@ type # please make sure we have under 32 options
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
const
|
||||
harmlessOptions* = {optForceFullMake, optNoLinking, optRun,
|
||||
optUseColors, optStdout}
|
||||
harmlessOptions* = {optForceFullMake, optNoLinking, optRun, optUseColors, optStdout}
|
||||
genSubDir* = RelativeDir"nimcache"
|
||||
NimExt* = "nim"
|
||||
RodExt* = "rod"
|
||||
HtmlExt* = "html"
|
||||
JsonExt* = "json"
|
||||
TagsExt* = "tags"
|
||||
TexExt* = "tex"
|
||||
IniExt* = "ini"
|
||||
DefaultConfig* = RelativeFile"nim.cfg"
|
||||
DefaultConfigNims* = RelativeFile"config.nims"
|
||||
DocConfig* = RelativeFile"nimdoc.cfg"
|
||||
DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
|
||||
htmldocsDir* = htmldocsDirname.RelativeDir
|
||||
docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications
|
||||
oKeepVariableNames* = true
|
||||
spellSuggestSecretSauce* = -1
|
||||
|
||||
type
|
||||
TBackend* = enum
|
||||
@@ -124,7 +137,7 @@ type
|
||||
cmdTcc # run the project via TCC backend
|
||||
cmdCheck # semantic checking for whole project
|
||||
cmdParse # parse a single file (for debugging)
|
||||
cmdScan # scan a single file (for debugging)
|
||||
cmdRod # .rod to some text representation (for debugging)
|
||||
cmdIdeTools # ide tools (e.g. nimsuggest)
|
||||
cmdNimscript # evaluate nimscript
|
||||
cmdDoc0
|
||||
@@ -176,7 +189,8 @@ type
|
||||
## Note: this feature can't be localized with {.push.}
|
||||
vmopsDanger,
|
||||
strictFuncs,
|
||||
views
|
||||
views,
|
||||
strictNotNil
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -189,7 +203,7 @@ type
|
||||
## are not anymore.
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
|
||||
TSystemCC* = enum
|
||||
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
|
||||
@@ -269,6 +283,7 @@ type
|
||||
numberOfProcessors*: int # number of processors
|
||||
lastCmdTime*: float # when caas is enabled, we measure each command
|
||||
symbolFiles*: SymbolFilesOption
|
||||
spellSuggestMax*: int # max number of spelling suggestions for typos
|
||||
|
||||
cppDefines*: HashSet[string] # (*)
|
||||
headerFile*: string
|
||||
@@ -367,8 +382,11 @@ proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
|
||||
if enabled: incl(conf.notes, note) else: excl(conf.notes, note)
|
||||
|
||||
proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
|
||||
# ternary states instead of binary states would simplify logic
|
||||
if optHints notin conf.options: false
|
||||
elif note in {hintConf}: # could add here other special notes like hintSource
|
||||
elif note in {hintConf, hintProcessing}:
|
||||
# could add here other special notes like hintSource
|
||||
# these notes apply globally.
|
||||
note in conf.mainPackageNotes
|
||||
else: note in conf.notes
|
||||
|
||||
@@ -377,11 +395,12 @@ proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool =
|
||||
|
||||
proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
|
||||
|
||||
template depConfigFields*(fn) {.dirty.} =
|
||||
fn(target)
|
||||
fn(options)
|
||||
fn(globalOptions)
|
||||
fn(selectedGC)
|
||||
when false:
|
||||
template depConfigFields*(fn) {.dirty.} = # deadcode
|
||||
fn(target)
|
||||
fn(options)
|
||||
fn(globalOptions)
|
||||
fn(selectedGC)
|
||||
|
||||
const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
|
||||
|
||||
@@ -424,6 +443,9 @@ template newPackageCache*(): untyped =
|
||||
proc newProfileData(): ProfileData =
|
||||
ProfileData(data: newTable[TLineInfo, ProfileInfo]())
|
||||
|
||||
const foreignPackageNotesDefault* = {
|
||||
hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting, hintUser, warnUser}
|
||||
|
||||
proc newConfigRef*(): ConfigRef =
|
||||
result = ConfigRef(
|
||||
selectedGC: gcRefc,
|
||||
@@ -435,8 +457,7 @@ proc newConfigRef*(): ConfigRef =
|
||||
arcToExpand: newStringTable(modeStyleInsensitive),
|
||||
m: initMsgConfig(),
|
||||
cppDefines: initHashSet[string](),
|
||||
headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
|
||||
hintQuitCalled, hintExecuting},
|
||||
headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: foreignPackageNotesDefault,
|
||||
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
|
||||
configVars: newStringTable(modeStyleInsensitive),
|
||||
symbols: newStringTable(modeStyleInsensitive),
|
||||
@@ -477,6 +498,7 @@ proc newConfigRef*(): ConfigRef =
|
||||
suggestMaxResults: 10_000,
|
||||
maxLoopIterationsVM: 10_000_000,
|
||||
vmProfileData: newProfileData(),
|
||||
spellSuggestMax: spellSuggestSecretSauce,
|
||||
)
|
||||
setTargetFromSystem(result.target)
|
||||
# enable colors by default on terminals
|
||||
@@ -490,8 +512,7 @@ proc newPartialConfigRef*(): ConfigRef =
|
||||
verbosity: 1,
|
||||
options: DefaultOptions,
|
||||
globalOptions: DefaultGlobalOptions,
|
||||
foreignPackageNotes: {hintProcessing, warnUnknownMagic,
|
||||
hintQuitCalled, hintExecuting},
|
||||
foreignPackageNotes: foreignPackageNotesDefault,
|
||||
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
|
||||
|
||||
proc cppDefine*(c: ConfigRef; define: string) =
|
||||
@@ -555,23 +576,6 @@ template compilationCachePresent*(conf: ConfigRef): untyped =
|
||||
template optPreserveOrigSource*(conf: ConfigRef): untyped =
|
||||
optEmbedOrigSrc in conf.globalOptions
|
||||
|
||||
const
|
||||
genSubDir* = RelativeDir"nimcache"
|
||||
NimExt* = "nim"
|
||||
RodExt* = "rod"
|
||||
HtmlExt* = "html"
|
||||
JsonExt* = "json"
|
||||
TagsExt* = "tags"
|
||||
TexExt* = "tex"
|
||||
IniExt* = "ini"
|
||||
DefaultConfig* = RelativeFile"nim.cfg"
|
||||
DefaultConfigNims* = RelativeFile"config.nims"
|
||||
DocConfig* = RelativeFile"nimdoc.cfg"
|
||||
DocTexConfig* = RelativeFile"nimdoc.tex.cfg"
|
||||
htmldocsDir* = htmldocsDirname.RelativeDir
|
||||
docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications
|
||||
oKeepVariableNames* = true
|
||||
|
||||
proc mainCommandArg*(conf: ConfigRef): string =
|
||||
## This is intended for commands like check or parse
|
||||
## which will work on the main project file unless
|
||||
@@ -718,6 +722,10 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
|
||||
result = subdir / RelativeFile f.string.splitPath.tail
|
||||
#echo "completeGeneratedFilePath(", f, ") = ", result
|
||||
|
||||
proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
|
||||
result = changeFileExt(completeGeneratedFilePath(conf,
|
||||
withPackageName(conf, f)), ext)
|
||||
|
||||
proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
|
||||
for it in conf.searchPaths:
|
||||
if suppressStdlib and it.string.startsWith(conf.libpath.string):
|
||||
|
||||
@@ -44,7 +44,8 @@ proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
# in different directory get different name and they can be
|
||||
# placed in a directory.
|
||||
# foo-#head/../bar becomes @foo-@hhead@s..@sbar
|
||||
result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace({$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace(
|
||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
|
||||
proc demanglePackageName*(path: string): string =
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
|
||||
|
||||
@@ -164,8 +164,6 @@ proc validInd(p: var Parser): bool {.inline.} =
|
||||
proc rawSkipComment(p: var Parser, node: PNode) =
|
||||
if p.tok.tokType == tkComment:
|
||||
if node != nil:
|
||||
when not defined(nimNoNilSeqs):
|
||||
if node.comment == nil: node.comment = ""
|
||||
when defined(nimpretty):
|
||||
if p.tok.commentOffsetB > p.tok.commentOffsetA:
|
||||
node.comment.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
|
||||
@@ -1310,6 +1308,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
|
||||
#| | IND{=} 'of' exprList ':' stmt
|
||||
#| | IND{=} 'elif' expr ':' stmt
|
||||
#| | IND{=} 'except' exprList ':' stmt
|
||||
#| | IND{=} 'finally' ':' stmt
|
||||
#| | IND{=} 'else' ':' stmt )*
|
||||
result = x
|
||||
if p.tok.indent >= 0: return
|
||||
@@ -1364,6 +1363,9 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
|
||||
of tkExcept:
|
||||
nextBlock = newNodeP(nkExceptBranch, p)
|
||||
exprList(p, tkColon, nextBlock)
|
||||
of tkFinally:
|
||||
nextBlock = newNodeP(nkFinally, p)
|
||||
getTok(p)
|
||||
of tkElse:
|
||||
nextBlock = newNodeP(nkElse, p)
|
||||
getTok(p)
|
||||
@@ -1374,7 +1376,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
|
||||
nextBlock.flags.incl nfBlockArg
|
||||
result.add nextBlock
|
||||
|
||||
if nextBlock.kind == nkElse: break
|
||||
if nextBlock.kind in {nkElse, nkFinally}: break
|
||||
else:
|
||||
if openingParams.kind != nkEmpty:
|
||||
parMessage(p, "expected ':'")
|
||||
@@ -1998,12 +2000,18 @@ proc parseTypeClass(p: var Parser): PNode =
|
||||
#| &IND{>} stmt
|
||||
result = newNodeP(nkTypeClassTy, p)
|
||||
getTok(p)
|
||||
var args = newNodeP(nkArgList, p)
|
||||
result.add(args)
|
||||
args.add(p.parseTypeClassParam)
|
||||
while p.tok.tokType == tkComma:
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkComment:
|
||||
skipComment(p, result)
|
||||
|
||||
if p.tok.indent < 0:
|
||||
var args = newNodeP(nkArgList, p)
|
||||
result.add(args)
|
||||
args.add(p.parseTypeClassParam)
|
||||
while p.tok.tokType == tkComma:
|
||||
getTok(p)
|
||||
args.add(p.parseTypeClassParam)
|
||||
else:
|
||||
result.add(p.emptyNode) # see ast.isNewStyleConcept
|
||||
if p.tok.tokType == tkCurlyDotLe and p.validInd:
|
||||
result.add(parsePragma(p))
|
||||
else:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import
|
||||
options, ast, llstream, msgs,
|
||||
idents,
|
||||
syntaxes, modulegraphs, reorder, rod,
|
||||
syntaxes, modulegraphs, reorder,
|
||||
lineinfos, pathutils
|
||||
|
||||
type
|
||||
@@ -108,7 +108,14 @@ proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
|
||||
graph.config.notes = graph.config.foreignPackageNotes
|
||||
|
||||
proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
|
||||
result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
|
||||
result = true
|
||||
#module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
|
||||
|
||||
proc partOfStdlib(x: PSym): bool =
|
||||
var it = x.owner
|
||||
while it != nil and it.kind == skPackage and it.owner != nil:
|
||||
it = it.owner
|
||||
result = it != nil and it.name.s == "stdlib"
|
||||
|
||||
proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream): bool {.discardable.} =
|
||||
@@ -119,88 +126,65 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
prepareConfigNotes(graph, module)
|
||||
if module.id < 0:
|
||||
# new module caching mechanism:
|
||||
for i in 0..<graph.passes.len:
|
||||
if not isNil(graph.passes[i].open) and not graph.passes[i].isFrontend:
|
||||
a[i] = graph.passes[i].open(graph, module, idgen)
|
||||
else:
|
||||
a[i] = nil
|
||||
|
||||
if not graph.stopCompile():
|
||||
let n = loadNode(graph, module)
|
||||
var m = n
|
||||
for i in 0..<graph.passes.len:
|
||||
if not isNil(graph.passes[i].process) and not graph.passes[i].isFrontend:
|
||||
m = graph.passes[i].process(a[i], m)
|
||||
if isNil(m):
|
||||
break
|
||||
|
||||
var m: PNode = nil
|
||||
for i in 0..<graph.passes.len:
|
||||
if not isNil(graph.passes[i].close) and not graph.passes[i].isFrontend:
|
||||
m = graph.passes[i].close(graph, a[i], m)
|
||||
a[i] = nil
|
||||
openPasses(graph, a, module, idgen)
|
||||
if stream == nil:
|
||||
let filename = toFullPathConsiderDirty(graph.config, fileIdx)
|
||||
s = llStreamOpen(filename, fmRead)
|
||||
if s == nil:
|
||||
rawMessage(graph.config, errCannotOpenFile, filename.string)
|
||||
return false
|
||||
else:
|
||||
openPasses(graph, a, module, idgen)
|
||||
if stream == nil:
|
||||
let filename = toFullPathConsiderDirty(graph.config, fileIdx)
|
||||
s = llStreamOpen(filename, fmRead)
|
||||
if s == nil:
|
||||
rawMessage(graph.config, errCannotOpenFile, filename.string)
|
||||
return false
|
||||
else:
|
||||
s = stream
|
||||
s = stream
|
||||
while true:
|
||||
openParser(p, fileIdx, s, graph.cache, graph.config)
|
||||
|
||||
if not partOfStdlib(module) or module.name.s == "distros":
|
||||
# XXX what about caching? no processing then? what if I change the
|
||||
# modules to include between compilation runs? we'd need to track that
|
||||
# in ROD files. I think we should enable this feature only
|
||||
# for the interactive mode.
|
||||
if module.name.s != "nimscriptapi":
|
||||
processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module
|
||||
processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
|
||||
|
||||
while true:
|
||||
openParser(p, fileIdx, s, graph.cache, graph.config)
|
||||
|
||||
if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros":
|
||||
# XXX what about caching? no processing then? what if I change the
|
||||
# modules to include between compilation runs? we'd need to track that
|
||||
# in ROD files. I think we should enable this feature only
|
||||
# for the interactive mode.
|
||||
if module.name.s != "nimscriptapi":
|
||||
processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module
|
||||
processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
|
||||
|
||||
while true:
|
||||
if graph.stopCompile(): break
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
if (sfSystemModule notin module.flags and
|
||||
({sfNoForward, sfReorder} * module.flags != {} or
|
||||
codeReordering in graph.config.features)):
|
||||
# read everything, no streaming possible
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
if graph.stopCompile(): break
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
if (sfSystemModule notin module.flags and
|
||||
({sfNoForward, sfReorder} * module.flags != {} or
|
||||
codeReordering in graph.config.features)):
|
||||
# read everything, no streaming possible
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
sl.add n
|
||||
while true:
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
sl.add n
|
||||
while true:
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
sl.add n
|
||||
if sfReorder in module.flags or codeReordering in graph.config.features:
|
||||
sl = reorder(graph, sl, module)
|
||||
discard processTopLevelStmt(graph, sl, a)
|
||||
break
|
||||
elif n.kind in imperativeCode:
|
||||
# read everything until the next proc declaration etc.
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
if sfReorder in module.flags or codeReordering in graph.config.features:
|
||||
sl = reorder(graph, sl, module)
|
||||
discard processTopLevelStmt(graph, sl, a)
|
||||
break
|
||||
elif n.kind in imperativeCode:
|
||||
# read everything until the next proc declaration etc.
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
sl.add n
|
||||
var rest: PNode = nil
|
||||
while true:
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty or n.kind notin imperativeCode:
|
||||
rest = n
|
||||
break
|
||||
sl.add n
|
||||
var rest: PNode = nil
|
||||
while true:
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty or n.kind notin imperativeCode:
|
||||
rest = n
|
||||
break
|
||||
sl.add n
|
||||
#echo "-----\n", sl
|
||||
if not processTopLevelStmt(graph, sl, a): break
|
||||
if rest != nil:
|
||||
#echo "-----\n", rest
|
||||
if not processTopLevelStmt(graph, rest, a): break
|
||||
else:
|
||||
#echo "----- single\n", n
|
||||
if not processTopLevelStmt(graph, n, a): break
|
||||
closeParser(p)
|
||||
if s.kind != llsStdIn: break
|
||||
closePasses(graph, a)
|
||||
#echo "-----\n", sl
|
||||
if not processTopLevelStmt(graph, sl, a): break
|
||||
if rest != nil:
|
||||
#echo "-----\n", rest
|
||||
if not processTopLevelStmt(graph, rest, a): break
|
||||
else:
|
||||
#echo "----- single\n", n
|
||||
if not processTopLevelStmt(graph, n, a): break
|
||||
closeParser(p)
|
||||
if s.kind != llsStdIn: break
|
||||
closePasses(graph, a)
|
||||
result = true
|
||||
|
||||
@@ -210,11 +210,8 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
|
||||
for j in 0..<p.len:
|
||||
if not matches(c, p[j], n[i+j]):
|
||||
# we need to undo any bindings:
|
||||
when defined(nimNoNilSeqs):
|
||||
c.mapping = @[]
|
||||
c.mappingIsFull = false
|
||||
else:
|
||||
if not isNil(c.mapping): c.mapping = nil
|
||||
c.mapping = @[]
|
||||
c.mappingIsFull = false
|
||||
return false
|
||||
result = true
|
||||
|
||||
|
||||
@@ -214,13 +214,14 @@ const
|
||||
(name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "hppa", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
|
||||
(name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), # a.k.a. x86_64, covers both amd and intel
|
||||
(name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
|
||||
(name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
|
||||
(name: "js", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
|
||||
# xxx this seems buggy; on a 64bit machine, sizeof(int) is 64 in nimvm.
|
||||
(name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
|
||||
(name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
|
||||
(name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## Plugin to transform an inline iterator into a data structure.
|
||||
|
||||
import ".." / [ast, lookups, semdata, lambdalifting, msgs]
|
||||
import ".." / [ast, modulegraphs, lookups, semdata, lambdalifting, msgs]
|
||||
|
||||
proc iterToProcImpl*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
@@ -29,10 +29,10 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
|
||||
localError(c.config, n[2].info,
|
||||
"type must be a non-generic ref|ptr to object with state field")
|
||||
return
|
||||
let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t, c.idgen)
|
||||
let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen)
|
||||
|
||||
let prc = newSym(skProc, n[3].ident, nextId c.idgen, iter.sym.owner, iter.sym.info)
|
||||
prc.typ = copyType(iter.sym.typ, nextId c.idgen, prc)
|
||||
let prc = newSym(skProc, n[3].ident, nextSymId c.idgen, iter.sym.owner, iter.sym.info)
|
||||
prc.typ = copyType(iter.sym.typ, nextTypeId c.idgen, prc)
|
||||
excl prc.typ.flags, tfCapturesEnv
|
||||
prc.typ.n.add newSymNode(getEnvParam(iter.sym))
|
||||
prc.typ.rawAddSon t
|
||||
|
||||
@@ -26,7 +26,7 @@ proc semLocals*(c: PContext, n: PNode): PNode =
|
||||
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}:
|
||||
|
||||
if it.owner == owner:
|
||||
var field = newSym(skField, it.name, nextId c.idgen, owner, n.info)
|
||||
var field = newSym(skField, it.name, nextSymId c.idgen, owner, n.info)
|
||||
field.typ = it.typ.skipTypes({tyVar})
|
||||
field.position = counter
|
||||
inc(counter)
|
||||
|
||||
@@ -14,13 +14,15 @@ import
|
||||
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
|
||||
types, lookups, lineinfos, pathutils, linter
|
||||
|
||||
from ic / ic import addCompilerProc
|
||||
|
||||
const
|
||||
FirstCallConv* = wNimcall
|
||||
LastCallConv* = wNoconv
|
||||
|
||||
const
|
||||
declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp,
|
||||
wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed, wAlign}
|
||||
wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed}
|
||||
## common pragmas for declarations, to a good approximation
|
||||
procPragmas* = declPragmas + {FirstCallConv..LastCallConv,
|
||||
wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
|
||||
@@ -54,22 +56,23 @@ const
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
|
||||
wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert}
|
||||
lambdaPragmas* = declPragmas + {FirstCallConv..LastCallConv,
|
||||
lambdaPragmas* = {FirstCallConv..LastCallConv,
|
||||
wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
|
||||
wThread, wAsmNoStackFrame,
|
||||
wRaises, wLocks, wTags, wRequires, wEnsures,
|
||||
wGcSafe, wCodegenDecl} - {wExportNims, wError, wUsed} # why exclude these?
|
||||
wGcSafe, wCodegenDecl, wNoInit, wCompileTime}
|
||||
typePragmas* = declPragmas + {wMagic, wAcyclic,
|
||||
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
|
||||
wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
|
||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||
fieldPragmas* = declPragmas + {
|
||||
wGuard, wBitsize, wCursor, wRequiresInit, wNoalias} - {wExportNims, wNodecl} # why exclude these?
|
||||
wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||
fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor,
|
||||
wRequiresInit, wNoalias, wAlign} - {wExportNims, wNodecl} # why exclude these?
|
||||
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
||||
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
||||
wNoInit, wCompileTime, wGlobal,
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias}
|
||||
wGensym, wInject, wCodegenDecl,
|
||||
wGuard, wGoto, wCursor, wNoalias, wAlign}
|
||||
constPragmas* = declPragmas + {wHeader, wMagic,
|
||||
wGensym, wInject,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
||||
@@ -94,10 +97,10 @@ proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool = false)
|
||||
|
||||
proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
|
||||
var recorded = newNodeI(nkCommentStmt, n.info)
|
||||
var recorded = newNodeI(nkReplayAction, n.info)
|
||||
for i in 0..args.high:
|
||||
recorded.add newStrNode(args[i], n.info)
|
||||
c.graph.recordStmt(c.graph, c.module, recorded)
|
||||
addPragmaComputation(c, recorded)
|
||||
|
||||
const
|
||||
errStringLiteralExpected = "string literal expected"
|
||||
@@ -121,7 +124,7 @@ proc pragmaEnsures(c: PContext, n: PNode) =
|
||||
openScope(c)
|
||||
let o = getCurrOwner(c)
|
||||
if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil:
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), nextId(c.idgen), o, n.info)
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId(c.idgen), o, n.info)
|
||||
s.typ = o.typ.sons[0]
|
||||
incl(s.flags, sfUsed)
|
||||
addDecl(c, s)
|
||||
@@ -350,6 +353,7 @@ proc processNote(c: PContext, n: PNode) =
|
||||
of wHint: handleNote(hintMin .. hintMax, c.config.notes)
|
||||
of wWarning: handleNote(warnMin .. warnMax, c.config.notes)
|
||||
of wWarningAsError: handleNote(warnMin .. warnMax, c.config.warningAsErrors)
|
||||
of wHintAsError: handleNote(hintMin .. hintMax, c.config.warningAsErrors)
|
||||
else: invalidPragma(c, n)
|
||||
else: invalidPragma(c, n)
|
||||
|
||||
@@ -563,8 +567,6 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||
var e = searchInScopes(con, getIdent(con.cache, sub), amb)
|
||||
# XXX what to do here if 'amb' is true?
|
||||
if e != nil:
|
||||
when false:
|
||||
if e.kind == skStub: loadStub(e)
|
||||
incl(e.flags, sfUsed)
|
||||
result.add newSymNode(e)
|
||||
else:
|
||||
@@ -638,7 +640,7 @@ proc processPragma(c: PContext, n: PNode, i: int) =
|
||||
elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
|
||||
invalidPragma(c, n)
|
||||
|
||||
var userPragma = newSym(skTemplate, it[1].ident, nextId(c.idgen), nil, it.info, c.config.options)
|
||||
var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), nil, it.info, c.config.options)
|
||||
userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
|
||||
strTableAdd(c.userPragmas, userPragma)
|
||||
|
||||
@@ -704,6 +706,8 @@ proc markCompilerProc(c: PContext; s: PSym) =
|
||||
incl(s.flags, sfCompilerProc)
|
||||
incl(s.flags, sfUsed)
|
||||
registerCompilerProc(c.graph, s)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addCompilerProc(c.encoder, c.packedRepr, s)
|
||||
|
||||
proc deprecatedStmt(c: PContext; outerPragma: PNode) =
|
||||
let pragma = outerPragma[1]
|
||||
@@ -719,7 +723,7 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
|
||||
if dest == nil or dest.kind in routineKinds:
|
||||
localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
|
||||
let src = considerQuotedIdent(c, n[0])
|
||||
let alias = newSym(skAlias, src, nextId(c.idgen), dest, n[0].info, c.config.options)
|
||||
let alias = newSym(skAlias, src, nextSymId(c.idgen), dest, n[0].info, c.config.options)
|
||||
incl(alias.flags, sfExported)
|
||||
if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
|
||||
addInterfaceDecl(c, alias)
|
||||
@@ -741,7 +745,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||
# We return a dummy symbol; later passes over the type will repair it.
|
||||
# Generic instantiation needs to know about this too. But we're lazy
|
||||
# and perform the lookup on demand instead.
|
||||
result = newSym(skUnknown, considerQuotedIdent(c, n), nextId(c.idgen), nil, n.info,
|
||||
result = newSym(skUnknown, considerQuotedIdent(c, n), nextSymId(c.idgen), nil, n.info,
|
||||
c.config.options)
|
||||
else:
|
||||
result = qualifiedLookUp(c, n, {checkUndeclared})
|
||||
@@ -840,6 +844,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else: invalidPragma(c, it)
|
||||
of wImportCpp:
|
||||
processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
|
||||
of wCppNonPod:
|
||||
incl(sym.flags, sfCppNonPod)
|
||||
of wImportJs:
|
||||
if c.config.backend != backendJs:
|
||||
localError(c.config, it.info, "`importjs` pragma requires the JavaScript target")
|
||||
@@ -1239,23 +1245,23 @@ proc mergePragmas(n, pragmas: PNode) =
|
||||
else:
|
||||
for p in pragmas: n[pragmasPos].add p
|
||||
|
||||
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||
proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
|
||||
validPragmas: TSpecialWords) =
|
||||
if sym != nil and sym.kind != skModule:
|
||||
for it in c.optionStack:
|
||||
let o = it.otherPragmas
|
||||
if not o.isNil and sfFromGeneric notin sym.flags: # see issue #12985
|
||||
pushInfoContext(c.config, n.info)
|
||||
pushInfoContext(c.config, info)
|
||||
var i = 0
|
||||
while i < o.len:
|
||||
if singlePragma(c, sym, o, i, validPragmas, true, false):
|
||||
internalError(c.config, n.info, "implicitPragmas")
|
||||
internalError(c.config, info, "implicitPragmas")
|
||||
inc i
|
||||
popInfoContext(c.config)
|
||||
if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
|
||||
|
||||
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
|
||||
localError(c.config, n.info, ".dynlib requires .exportc")
|
||||
localError(c.config, info, ".dynlib requires .exportc")
|
||||
var lib = c.optionStack[^1].dynlib
|
||||
if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
|
||||
sfImportc in sym.flags and lib != nil:
|
||||
@@ -1285,4 +1291,11 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool) =
|
||||
if n == nil: return
|
||||
pragmaRec(c, sym, n, validPragmas, isStatement)
|
||||
implicitPragmas(c, sym, n, validPragmas)
|
||||
# XXX: in the case of a callable def, this should use its info
|
||||
implicitPragmas(c, sym, n.info, validPragmas)
|
||||
|
||||
proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
|
||||
isStatement: bool = false) =
|
||||
if n == nil: return
|
||||
if n[pragmasPos].kind != nkEmpty:
|
||||
pragmaRec(c, sym, n[pragmasPos], validPragmas, isStatement)
|
||||
|
||||
@@ -203,18 +203,18 @@ proc putComment(g: var TSrcGen, s: string) =
|
||||
case s[i]
|
||||
of '\0':
|
||||
break
|
||||
of '\x0D':
|
||||
of '\r':
|
||||
put(g, tkComment, com)
|
||||
com = "## "
|
||||
inc(i)
|
||||
if i <= hi and s[i] == '\x0A': inc(i)
|
||||
if i <= hi and s[i] == '\n': inc(i)
|
||||
optNL(g, ind)
|
||||
of '\x0A':
|
||||
of '\n':
|
||||
put(g, tkComment, com)
|
||||
com = "## "
|
||||
inc(i)
|
||||
optNL(g, ind)
|
||||
of ' ', '\x09':
|
||||
of ' ', '\t':
|
||||
com.add(s[i])
|
||||
inc(i)
|
||||
else:
|
||||
@@ -242,12 +242,12 @@ proc maxLineLength(s: string): int =
|
||||
case s[i]
|
||||
of '\0':
|
||||
break
|
||||
of '\x0D':
|
||||
of '\r':
|
||||
inc(i)
|
||||
if i <= hi and s[i] == '\x0A': inc(i)
|
||||
if i <= hi and s[i] == '\n': inc(i)
|
||||
result = max(result, lineLen)
|
||||
lineLen = 0
|
||||
of '\x0A':
|
||||
of '\n':
|
||||
inc(i)
|
||||
result = max(result, lineLen)
|
||||
lineLen = 0
|
||||
@@ -261,13 +261,13 @@ proc putRawStr(g: var TSrcGen, kind: TokType, s: string) =
|
||||
var str = ""
|
||||
while i <= hi:
|
||||
case s[i]
|
||||
of '\x0D':
|
||||
of '\r':
|
||||
put(g, kind, str)
|
||||
str = ""
|
||||
inc(i)
|
||||
if i <= hi and s[i] == '\x0A': inc(i)
|
||||
if i <= hi and s[i] == '\n': inc(i)
|
||||
optNL(g, 0)
|
||||
of '\x0A':
|
||||
of '\n':
|
||||
put(g, kind, str)
|
||||
str = ""
|
||||
inc(i)
|
||||
@@ -280,7 +280,7 @@ proc putRawStr(g: var TSrcGen, kind: TokType, s: string) =
|
||||
proc containsNL(s: string): bool =
|
||||
for i in 0..<s.len:
|
||||
case s[i]
|
||||
of '\x0D', '\x0A':
|
||||
of '\r', '\n':
|
||||
return true
|
||||
else:
|
||||
discard
|
||||
@@ -296,11 +296,11 @@ proc popAllComs(g: var TSrcGen) =
|
||||
const
|
||||
Space = " "
|
||||
|
||||
proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
|
||||
result = false
|
||||
if n.comment.len > 0:
|
||||
result = (renderNoComments notin g.flags) or
|
||||
(renderDocComments in g.flags)
|
||||
proc shouldRenderComment(g: TSrcGen): bool {.inline.} =
|
||||
(renderNoComments notin g.flags or renderDocComments in g.flags)
|
||||
|
||||
proc shouldRenderComment(g: TSrcGen, n: PNode): bool {.inline.} =
|
||||
shouldRenderComment(g) and n.comment.len > 0
|
||||
|
||||
proc gcom(g: var TSrcGen, n: PNode) =
|
||||
assert(n != nil)
|
||||
@@ -430,7 +430,7 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
||||
proc lsub(g: TSrcGen; n: PNode): int =
|
||||
# computes the length of a tree
|
||||
if isNil(n): return 0
|
||||
if n.comment.len > 0: return MaxLineLen + 1
|
||||
if shouldRenderComment(g, n): return MaxLineLen + 1
|
||||
case n.kind
|
||||
of nkEmpty: result = 0
|
||||
of nkTripleStrLit:
|
||||
@@ -598,7 +598,7 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
|
||||
if c:
|
||||
if g.tokens.len > oldLen:
|
||||
putWithSpace(g, separator, $separator)
|
||||
if hasCom(n[i]):
|
||||
if shouldRenderComment(g) and hasCom(n[i]):
|
||||
gcoms(g)
|
||||
optNL(g, ind)
|
||||
|
||||
@@ -639,7 +639,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TokType,
|
||||
dedent(g)
|
||||
|
||||
proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
|
||||
result = n.comment.len > 0
|
||||
result = shouldRenderComment(g, n)
|
||||
if not result:
|
||||
# check further
|
||||
for i in start..n.len + theEnd:
|
||||
@@ -820,14 +820,28 @@ proc gTypeClassTy(g: var TSrcGen, n: PNode) =
|
||||
dedent(g)
|
||||
|
||||
proc gblock(g: var TSrcGen, n: PNode) =
|
||||
# you shouldn't simplify it to `n.len < 2`
|
||||
# because the following codes should be executed
|
||||
# even when block stmt has only one child for getting
|
||||
# better error messages.
|
||||
if n.len == 0:
|
||||
return
|
||||
|
||||
var c: TContext
|
||||
initContext(c)
|
||||
|
||||
if n[0].kind != nkEmpty:
|
||||
putWithSpace(g, tkBlock, "block")
|
||||
gsub(g, n[0])
|
||||
else:
|
||||
put(g, tkBlock, "block")
|
||||
|
||||
# block stmt should have two children
|
||||
if n.len == 1:
|
||||
return
|
||||
|
||||
putWithSpace(g, tkColon, ":")
|
||||
|
||||
if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
|
||||
incl(c.flags, rfLongMode)
|
||||
gcoms(g)
|
||||
@@ -966,7 +980,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
if isNil(n): return
|
||||
var
|
||||
a: TContext
|
||||
if n.comment.len > 0: pushCom(g, n)
|
||||
if shouldRenderComment(g, n): pushCom(g, n)
|
||||
case n.kind # atoms:
|
||||
of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
|
||||
of nkEmpty: discard
|
||||
@@ -1129,9 +1143,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
gcomma(g, n)
|
||||
put(g, tkParRi, ")")
|
||||
of nkObjDownConv, nkObjUpConv:
|
||||
let typ = if (n.typ != nil) and (n.typ.sym != nil): n.typ.sym.name.s else: ""
|
||||
put(g, tkParLe, typ & "(")
|
||||
if n.len >= 1: gsub(g, n[0])
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n, 1)
|
||||
put(g, tkParRi, ")")
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
if renderIds in g.flags:
|
||||
@@ -1633,7 +1647,7 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
|
||||
|
||||
proc `$`*(n: PNode): string = n.renderTree
|
||||
|
||||
proc renderModule*(n: PNode, infile, outfile: string,
|
||||
proc renderModule*(n: PNode, outfile: string,
|
||||
renderFlags: TRenderFlags = {};
|
||||
fid = FileIndex(-1);
|
||||
conf: ConfigRef = nil) =
|
||||
|
||||
@@ -105,6 +105,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
|
||||
decl(a[1])
|
||||
else:
|
||||
for i in 0..<n.safeLen: deps(n[i])
|
||||
of nkMixinStmt, nkBindStmt: discard
|
||||
else:
|
||||
for i in 0..<n.safeLen: deps(n[i])
|
||||
|
||||
@@ -190,7 +191,7 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
|
||||
# their original relative order and make sure to re-merge
|
||||
# consecutive type and const sections
|
||||
var wmsg = "Circular dependency detected. `codeReordering` pragma may not be able to" &
|
||||
" reorder some nodes properely"
|
||||
" reorder some nodes properly"
|
||||
when defined(debugReorder):
|
||||
wmsg &= ":\n"
|
||||
for i in 0..<cs.len-1:
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2017 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the canonalization for the various caching mechanisms.
|
||||
|
||||
import ast, lineinfos, incremental, modulegraphs, pathutils
|
||||
|
||||
when not nimIncremental:
|
||||
template setupModuleCache*(g: ModuleGraph) = discard
|
||||
template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard
|
||||
template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList)
|
||||
|
||||
proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): (PSym) {.inline.} = nil
|
||||
|
||||
template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard
|
||||
|
||||
template storeRemaining*(g: ModuleGraph; module: PSym) = discard
|
||||
|
||||
#template registerModule*(g: ModuleGraph; module: PSym) = discard
|
||||
|
||||
else:
|
||||
include rodimpl
|
||||
|
||||
# idea for testing all this logic: *Always* load the AST from the DB, whether
|
||||
# we already have it in RAM or not!
|
||||
@@ -1,950 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2018 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the new compilation cache.
|
||||
|
||||
import strutils, intsets, tables, ropes, db_sqlite, msgs, options,
|
||||
renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
|
||||
btrees, trees, condsyms, nversion, pathutils
|
||||
|
||||
## Todo:
|
||||
## - Dependency computation should use *signature* hashes in order to
|
||||
## avoid recompiling dependent modules.
|
||||
## - Patch the rest of the compiler to do lazy loading of proc bodies.
|
||||
## - serialize the AST in a smarter way (avoid storing some ASTs twice!)
|
||||
|
||||
template db(): DbConn = g.incr.db
|
||||
|
||||
proc encodeConfig(g: ModuleGraph): string =
|
||||
result = newStringOfCap(100)
|
||||
result.add RodFileVersion
|
||||
for d in definedSymbolNames(g.config.symbols):
|
||||
result.add ' '
|
||||
result.add d
|
||||
|
||||
template serialize(field) =
|
||||
result.add ' '
|
||||
result.add($g.config.field)
|
||||
|
||||
depConfigFields(serialize)
|
||||
|
||||
proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile;
|
||||
cycleCheck: var IntSet): bool =
|
||||
let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
|
||||
fullpath.string)
|
||||
if root[0].len == 0: return true
|
||||
if root[1] != hashFileCached(g.config, fileIdx, fullpath):
|
||||
return true
|
||||
# cycle detection: assume "not changed" is correct.
|
||||
if cycleCheck.containsOrIncl(int fileIdx):
|
||||
return false
|
||||
# check dependencies (recursively):
|
||||
for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)",
|
||||
root[0]):
|
||||
let dep = AbsoluteFile row[0]
|
||||
if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck):
|
||||
return true
|
||||
return false
|
||||
|
||||
proc getModuleId(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): int =
|
||||
## Analyse the known dependency graph.
|
||||
if g.config.symbolFiles == disabledSf: return getID()
|
||||
when false:
|
||||
if g.config.symbolFiles in {disabledSf, writeOnlySf} or
|
||||
g.incr.configChanged:
|
||||
return getID()
|
||||
let module = g.incr.db.getRow(
|
||||
sql"select id, fullHash, nimid from modules where fullpath = ?", string fullpath)
|
||||
let currentFullhash = hashFileCached(g.config, fileIdx, fullpath)
|
||||
if module[0].len == 0:
|
||||
result = getID()
|
||||
db.exec(sql"insert into modules(fullpath, interfHash, fullHash, nimid) values (?, ?, ?, ?)",
|
||||
string fullpath, "", currentFullhash, result)
|
||||
else:
|
||||
result = parseInt(module[2])
|
||||
if currentFullhash == module[1]:
|
||||
# not changed, so use the cached AST:
|
||||
doAssert(result != 0)
|
||||
var cycleCheck = initIntSet()
|
||||
if not needsRecompile(g, fileIdx, fullpath, cycleCheck):
|
||||
if not g.incr.configChanged or g.config.symbolFiles == readOnlySf:
|
||||
#echo "cached successfully! ", string fullpath
|
||||
return -result
|
||||
elif g.config.symbolFiles == readOnlySf:
|
||||
internalError(g.config, "file needs to be recompiled: " & (string fullpath))
|
||||
db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
|
||||
db.exec(sql"delete from deps where module = ?", module[0])
|
||||
db.exec(sql"delete from types where module = ?", module[0])
|
||||
db.exec(sql"delete from syms where module = ?", module[0])
|
||||
db.exec(sql"delete from toplevelstmts where module = ?", module[0])
|
||||
db.exec(sql"delete from statics where module = ?", module[0])
|
||||
|
||||
proc loadModuleSym*(g: ModuleGraph; fileIdx: FileIndex; fullpath: AbsoluteFile): PSym =
|
||||
let id = getModuleId(g, fileIdx, fullpath)
|
||||
result = g.incr.r.syms.getOrDefault(abs id)
|
||||
|
||||
proc pushType(w: var Writer, t: PType) =
|
||||
if not containsOrIncl(w.tmarks, t.uniqueId):
|
||||
w.tstack.add(t)
|
||||
|
||||
proc pushSym(w: var Writer, s: PSym) =
|
||||
if not containsOrIncl(w.smarks, s.id):
|
||||
w.sstack.add(s)
|
||||
|
||||
template w: untyped = g.incr.w
|
||||
|
||||
proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode,
|
||||
result: var string) =
|
||||
if n == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("()")
|
||||
return
|
||||
result.add('(')
|
||||
encodeVInt(ord(n.kind), result)
|
||||
# we do not write comments for now
|
||||
# Line information takes easily 20% or more of the filesize! Therefore we
|
||||
# omit line information if it is the same as the parent's line information:
|
||||
if fInfo.fileIndex != n.info.fileIndex:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(int n.info.line, result)
|
||||
result.add(',')
|
||||
#encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result)
|
||||
encodeVInt(n.info.fileIndex.int, result)
|
||||
elif fInfo.line != n.info.line:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(int n.info.line, result)
|
||||
elif fInfo.col != n.info.col:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
# No need to output the file index, as this is the serialization of one
|
||||
# file.
|
||||
let f = n.flags * PersistentNodeFlags
|
||||
if f != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](f), result)
|
||||
if n.typ != nil:
|
||||
result.add('^')
|
||||
encodeVInt(n.typ.uniqueId, result)
|
||||
pushType(w, n.typ)
|
||||
case n.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if n.intVal != 0:
|
||||
result.add('!')
|
||||
encodeVBiggestInt(n.intVal, result)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if n.floatVal != 0.0:
|
||||
result.add('!')
|
||||
encodeStr($n.floatVal, result)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if n.strVal != "":
|
||||
result.add('!')
|
||||
encodeStr(n.strVal, result)
|
||||
of nkIdent:
|
||||
result.add('!')
|
||||
encodeStr(n.ident.s, result)
|
||||
of nkSym:
|
||||
result.add('!')
|
||||
encodeVInt(n.sym.id, result)
|
||||
pushSym(w, n.sym)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
encodeNode(g, n.info, n[i], result)
|
||||
result.add(')')
|
||||
|
||||
proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) =
|
||||
var oldLen = result.len
|
||||
result.add('<')
|
||||
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
|
||||
if loc.storage != low(loc.storage):
|
||||
result.add('*')
|
||||
encodeVInt(ord(loc.storage), result)
|
||||
if loc.flags != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](loc.flags), result)
|
||||
if loc.lode != nil:
|
||||
result.add('^')
|
||||
encodeNode(g, unknownLineInfo, loc.lode, result)
|
||||
if loc.r != nil:
|
||||
result.add('!')
|
||||
encodeStr($loc.r, result)
|
||||
if oldLen + 1 == result.len:
|
||||
# no data was necessary, so remove the '<' again:
|
||||
setLen(result, oldLen)
|
||||
else:
|
||||
result.add('>')
|
||||
|
||||
proc encodeType(g: ModuleGraph, t: PType, result: var string) =
|
||||
if t == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("[]")
|
||||
return
|
||||
# we need no surrounding [] here because the type is in a line of its own
|
||||
if t.kind == tyForward: internalError(g.config, "encodeType: tyForward")
|
||||
# for the new rodfile viewer we use a preceding [ so that the data section
|
||||
# can easily be disambiguated:
|
||||
result.add('[')
|
||||
encodeVInt(ord(t.kind), result)
|
||||
result.add('+')
|
||||
encodeVInt(t.uniqueId, result)
|
||||
if t.id != t.uniqueId:
|
||||
result.add('+')
|
||||
encodeVInt(t.id, result)
|
||||
if t.n != nil:
|
||||
encodeNode(g, unknownLineInfo, t.n, result)
|
||||
if t.flags != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](t.flags), result)
|
||||
if t.callConv != low(t.callConv):
|
||||
result.add('?')
|
||||
encodeVInt(ord(t.callConv), result)
|
||||
if t.owner != nil:
|
||||
result.add('*')
|
||||
encodeVInt(t.owner.id, result)
|
||||
pushSym(w, t.owner)
|
||||
if t.sym != nil:
|
||||
result.add('&')
|
||||
encodeVInt(t.sym.id, result)
|
||||
pushSym(w, t.sym)
|
||||
if t.size != - 1:
|
||||
result.add('/')
|
||||
encodeVBiggestInt(t.size, result)
|
||||
if t.align != 2:
|
||||
result.add('=')
|
||||
encodeVInt(t.align, result)
|
||||
if t.lockLevel.ord != UnspecifiedLockLevel.ord:
|
||||
result.add('\14')
|
||||
encodeVInt(t.lockLevel.int16, result)
|
||||
if t.paddingAtEnd != 0:
|
||||
result.add('\15')
|
||||
encodeVInt(t.paddingAtEnd, result)
|
||||
for a in t.attachedOps:
|
||||
result.add('\16')
|
||||
if a == nil:
|
||||
encodeVInt(-1, result)
|
||||
else:
|
||||
encodeVInt(a.id, result)
|
||||
pushSym(w, a)
|
||||
for i, s in items(t.methods):
|
||||
result.add('\19')
|
||||
encodeVInt(i, result)
|
||||
result.add('\20')
|
||||
encodeVInt(s.id, result)
|
||||
pushSym(w, s)
|
||||
encodeLoc(g, t.loc, result)
|
||||
if t.typeInst != nil:
|
||||
result.add('\21')
|
||||
encodeVInt(t.typeInst.uniqueId, result)
|
||||
pushType(w, t.typeInst)
|
||||
for i in 0..<t.len:
|
||||
if t[i] == nil:
|
||||
result.add("^()")
|
||||
else:
|
||||
result.add('^')
|
||||
encodeVInt(t[i].uniqueId, result)
|
||||
pushType(w, t[i])
|
||||
|
||||
proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) =
|
||||
result.add('|')
|
||||
encodeVInt(ord(lib.kind), result)
|
||||
result.add('|')
|
||||
encodeStr($lib.name, result)
|
||||
result.add('|')
|
||||
encodeNode(g, info, lib.path, result)
|
||||
|
||||
proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation];
|
||||
result: var string) =
|
||||
for t in s:
|
||||
result.add('\15')
|
||||
encodeVInt(t.sym.id, result)
|
||||
pushSym(w, t.sym)
|
||||
for tt in t.concreteTypes:
|
||||
result.add('\17')
|
||||
encodeVInt(tt.uniqueId, result)
|
||||
pushType(w, tt)
|
||||
result.add('\20')
|
||||
encodeVInt(t.compilesId, result)
|
||||
|
||||
proc encodeSym(g: ModuleGraph, s: PSym, result: var string) =
|
||||
if s == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("{}")
|
||||
return
|
||||
# we need no surrounding {} here because the symbol is in a line of its own
|
||||
encodeVInt(ord(s.kind), result)
|
||||
result.add('+')
|
||||
encodeVInt(s.id, result)
|
||||
result.add('&')
|
||||
encodeStr(s.name.s, result)
|
||||
if s.typ != nil:
|
||||
result.add('^')
|
||||
encodeVInt(s.typ.uniqueId, result)
|
||||
pushType(w, s.typ)
|
||||
result.add('?')
|
||||
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
|
||||
result.add(',')
|
||||
encodeVInt(int s.info.line, result)
|
||||
result.add(',')
|
||||
#encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result)
|
||||
encodeVInt(s.info.fileIndex.int, result)
|
||||
if s.owner != nil:
|
||||
result.add('*')
|
||||
encodeVInt(s.owner.id, result)
|
||||
pushSym(w, s.owner)
|
||||
if s.flags != {}:
|
||||
result.add('$')
|
||||
encodeVBiggestInt(cast[int64](s.flags), result)
|
||||
if s.magic != mNone:
|
||||
result.add('@')
|
||||
encodeVInt(ord(s.magic), result)
|
||||
result.add('!')
|
||||
encodeVInt(cast[int32](s.options), result)
|
||||
if s.position != 0:
|
||||
result.add('%')
|
||||
encodeVInt(s.position, result)
|
||||
if s.offset != - 1:
|
||||
result.add('`')
|
||||
encodeVInt(s.offset, result)
|
||||
encodeLoc(g, s.loc, result)
|
||||
if s.annex != nil: encodeLib(g, s.annex, s.info, result)
|
||||
if s.constraint != nil:
|
||||
result.add('#')
|
||||
encodeNode(g, unknownLineInfo, s.constraint, result)
|
||||
case s.kind
|
||||
of skType, skGenericParam:
|
||||
for t in s.typeInstCache:
|
||||
result.add('\14')
|
||||
encodeVInt(t.uniqueId, result)
|
||||
pushType(w, t)
|
||||
of routineKinds:
|
||||
encodeInstantiations(g, s.procInstCache, result)
|
||||
if s.gcUnsafetyReason != nil:
|
||||
result.add('\16')
|
||||
encodeVInt(s.gcUnsafetyReason.id, result)
|
||||
pushSym(w, s.gcUnsafetyReason)
|
||||
if s.transformedBody != nil:
|
||||
result.add('\24')
|
||||
encodeNode(g, s.info, s.transformedBody, result)
|
||||
of skModule, skPackage:
|
||||
encodeInstantiations(g, s.usedGenerics, result)
|
||||
# we don't serialize:
|
||||
#tab*: TStrTable # interface table for modules
|
||||
of skLet, skVar, skField, skForVar:
|
||||
if s.guard != nil:
|
||||
result.add('\18')
|
||||
encodeVInt(s.guard.id, result)
|
||||
pushSym(w, s.guard)
|
||||
if s.bitsize != 0:
|
||||
result.add('\19')
|
||||
encodeVInt(s.bitsize, result)
|
||||
else: discard
|
||||
# lazy loading will soon reload the ast lazily, so the ast needs to be
|
||||
# the last entry of a symbol:
|
||||
if s.ast != nil:
|
||||
# we used to attempt to save space here by only storing a dummy AST if
|
||||
# it is not necessary, but Nim's heavy compile-time evaluation features
|
||||
# make that unfeasible nowadays:
|
||||
encodeNode(g, s.info, s.ast, result)
|
||||
|
||||
proc storeSym(g: ModuleGraph; s: PSym) =
|
||||
if sfForward in s.flags and s.kind != skModule:
|
||||
w.forwardedSyms.add s
|
||||
return
|
||||
var buf = newStringOfCap(160)
|
||||
encodeSym(g, s, buf)
|
||||
# XXX only store the name for exported symbols in order to speed up lookup
|
||||
# times once we enable the skStub logic.
|
||||
let m = getModule(s)
|
||||
let mid = if m == nil: 0 else: abs(m.id)
|
||||
db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)",
|
||||
s.id, mid, s.name.s, buf, ord(sfExported in s.flags))
|
||||
|
||||
proc storeType(g: ModuleGraph; t: PType) =
|
||||
var buf = newStringOfCap(160)
|
||||
encodeType(g, t, buf)
|
||||
let m = if t.owner != nil: getModule(t.owner) else: nil
|
||||
let mid = if m == nil: 0 else: abs(m.id)
|
||||
db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)",
|
||||
t.uniqueId, mid, buf)
|
||||
|
||||
proc transitiveClosure(g: ModuleGraph) =
|
||||
var i = 0
|
||||
while true:
|
||||
if i > 100_000:
|
||||
doAssert false, "loop never ends!"
|
||||
if w.sstack.len > 0:
|
||||
let s = w.sstack.pop()
|
||||
when false:
|
||||
echo "popped ", s.name.s, " ", s.id
|
||||
storeSym(g, s)
|
||||
elif w.tstack.len > 0:
|
||||
let t = w.tstack.pop()
|
||||
storeType(g, t)
|
||||
when false:
|
||||
echo "popped type ", typeToString(t), " ", t.uniqueId
|
||||
else:
|
||||
break
|
||||
inc i
|
||||
|
||||
proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) =
|
||||
if g.config.symbolFiles == disabledSf: return
|
||||
var buf = newStringOfCap(160)
|
||||
encodeNode(g, module.info, n, buf)
|
||||
db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)",
|
||||
abs(module.id), module.offset, buf)
|
||||
inc module.offset
|
||||
transitiveClosure(g)
|
||||
|
||||
proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) =
|
||||
storeNode(g, module, n)
|
||||
|
||||
proc storeFilename(g: ModuleGraph; fullpath: AbsoluteFile; fileIdx: FileIndex) =
|
||||
let id = db.getValue(sql"select id from filenames where fullpath = ?", fullpath.string)
|
||||
if id.len == 0:
|
||||
let fullhash = hashFileCached(g.config, fileIdx, fullpath)
|
||||
db.exec(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)",
|
||||
int(fileIdx), fullpath.string, fullhash)
|
||||
|
||||
proc storeRemaining*(g: ModuleGraph; module: PSym) =
|
||||
if g.config.symbolFiles == disabledSf: return
|
||||
var stillForwarded: seq[PSym] = @[]
|
||||
for s in w.forwardedSyms:
|
||||
if sfForward notin s.flags:
|
||||
storeSym(g, s)
|
||||
else:
|
||||
stillForwarded.add s
|
||||
swap w.forwardedSyms, stillForwarded
|
||||
transitiveClosure(g)
|
||||
var nimid = 0
|
||||
for x in items(g.config.m.fileInfos):
|
||||
storeFilename(g, x.fullPath, FileIndex(nimid))
|
||||
inc nimid
|
||||
|
||||
# ---------------- decoder -----------------------------------
|
||||
|
||||
type
|
||||
BlobReader = object
|
||||
s: string
|
||||
pos: int
|
||||
|
||||
using
|
||||
b: var BlobReader
|
||||
g: ModuleGraph
|
||||
|
||||
proc loadSym(g; id: int, info: TLineInfo): PSym
|
||||
proc loadType(g; id: int, info: TLineInfo): PType
|
||||
|
||||
proc decodeLineInfo(g; b; info: var TLineInfo) =
|
||||
if b.s[b.pos] == '?':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == ',': info.col = -1'i16
|
||||
else: info.col = int16(decodeVInt(b.s, b.pos))
|
||||
if b.s[b.pos] == ',':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == ',': info.line = 0'u16
|
||||
else: info.line = uint16(decodeVInt(b.s, b.pos))
|
||||
if b.s[b.pos] == ',':
|
||||
inc(b.pos)
|
||||
#info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos))
|
||||
info.fileIndex = FileIndex decodeVInt(b.s, b.pos)
|
||||
|
||||
proc skipNode(b) =
|
||||
# ')' itself cannot be part of a string literal so that this is correct.
|
||||
assert b.s[b.pos] == '('
|
||||
var par = 0
|
||||
var pos = b.pos+1
|
||||
while true:
|
||||
case b.s[pos]
|
||||
of ')':
|
||||
if par == 0: break
|
||||
dec par
|
||||
of '(': inc par
|
||||
else: discard
|
||||
inc pos
|
||||
b.pos = pos+1 # skip ')'
|
||||
|
||||
proc decodeNodeLazyBody(g; b; fInfo: TLineInfo,
|
||||
belongsTo: PSym): PNode =
|
||||
result = nil
|
||||
if b.s[b.pos] == '(':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == ')':
|
||||
inc(b.pos)
|
||||
return # nil node
|
||||
result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo)
|
||||
decodeLineInfo(g, b, result.info)
|
||||
if b.s[b.pos] == '$':
|
||||
inc(b.pos)
|
||||
result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos)))
|
||||
if b.s[b.pos] == '^':
|
||||
inc(b.pos)
|
||||
var id = decodeVInt(b.s, b.pos)
|
||||
result.typ = loadType(g, id, result.info)
|
||||
case result.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
result.intVal = decodeVBiggestInt(b.s, b.pos)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
var fl = decodeStr(b.s, b.pos)
|
||||
result.floatVal = parseFloat(fl)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
result.strVal = decodeStr(b.s, b.pos)
|
||||
else:
|
||||
result.strVal = ""
|
||||
of nkIdent:
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
var fl = decodeStr(b.s, b.pos)
|
||||
result.ident = g.cache.getIdent(fl)
|
||||
else:
|
||||
internalError(g.config, result.info, "decodeNode: nkIdent")
|
||||
of nkSym:
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
var id = decodeVInt(b.s, b.pos)
|
||||
result.sym = loadSym(g, id, result.info)
|
||||
else:
|
||||
internalError(g.config, result.info, "decodeNode: nkSym")
|
||||
else:
|
||||
var i = 0
|
||||
while b.s[b.pos] != ')':
|
||||
when false:
|
||||
if belongsTo != nil and i == bodyPos:
|
||||
addSonNilAllowed(result, nil)
|
||||
belongsTo.offset = b.pos
|
||||
skipNode(b)
|
||||
else:
|
||||
discard
|
||||
addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil))
|
||||
inc i
|
||||
if b.s[b.pos] == ')': inc(b.pos)
|
||||
else: internalError(g.config, result.info, "decodeNode: ')' missing")
|
||||
else:
|
||||
internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos)
|
||||
|
||||
proc decodeNode(g; b; fInfo: TLineInfo): PNode =
|
||||
result = decodeNodeLazyBody(g, b, fInfo, nil)
|
||||
|
||||
proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) =
|
||||
if b.s[b.pos] == '<':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
|
||||
loc.k = TLocKind(decodeVInt(b.s, b.pos))
|
||||
else:
|
||||
loc.k = low(loc.k)
|
||||
if b.s[b.pos] == '*':
|
||||
inc(b.pos)
|
||||
loc.storage = TStorageLoc(decodeVInt(b.s, b.pos))
|
||||
else:
|
||||
loc.storage = low(loc.storage)
|
||||
if b.s[b.pos] == '$':
|
||||
inc(b.pos)
|
||||
loc.flags = cast[TLocFlags](int32(decodeVInt(b.s, b.pos)))
|
||||
else:
|
||||
loc.flags = {}
|
||||
if b.s[b.pos] == '^':
|
||||
inc(b.pos)
|
||||
loc.lode = decodeNode(g, b, info)
|
||||
# rrGetType(b, decodeVInt(b.s, b.pos), info)
|
||||
else:
|
||||
loc.lode = nil
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
loc.r = rope(decodeStr(b.s, b.pos))
|
||||
else:
|
||||
loc.r = nil
|
||||
if b.s[b.pos] == '>': inc(b.pos)
|
||||
else: internalError(g.config, info, "decodeLoc " & b.s[b.pos])
|
||||
|
||||
proc loadBlob(g; query: SqlQuery; id: int): BlobReader =
|
||||
let blob = db.getValue(query, id)
|
||||
if blob.len == 0:
|
||||
internalError(g.config, "symbolfiles: cannot find ID " & $ id)
|
||||
result = BlobReader(pos: 0)
|
||||
shallowCopy(result.s, blob)
|
||||
# ensure we can read without index checks:
|
||||
result.s.add '\0'
|
||||
|
||||
proc loadType(g; id: int; info: TLineInfo): PType =
|
||||
result = g.incr.r.types.getOrDefault(id)
|
||||
if result != nil: return result
|
||||
var b = loadBlob(g, sql"select data from types where nimid = ?", id)
|
||||
|
||||
if b.s[b.pos] == '[':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == ']':
|
||||
inc(b.pos)
|
||||
return # nil type
|
||||
new(result)
|
||||
result.kind = TTypeKind(decodeVInt(b.s, b.pos))
|
||||
if b.s[b.pos] == '+':
|
||||
inc(b.pos)
|
||||
result.uniqueId = decodeVInt(b.s, b.pos)
|
||||
setId(result.uniqueId)
|
||||
#if debugIds: registerID(result)
|
||||
else:
|
||||
internalError(g.config, info, "decodeType: no id")
|
||||
if b.s[b.pos] == '+':
|
||||
inc(b.pos)
|
||||
result.id = decodeVInt(b.s, b.pos)
|
||||
else:
|
||||
result.id = result.uniqueId
|
||||
# here this also avoids endless recursion for recursive type
|
||||
g.incr.r.types.add(result.uniqueId, result)
|
||||
if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo)
|
||||
if b.s[b.pos] == '$':
|
||||
inc(b.pos)
|
||||
result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos)))
|
||||
if b.s[b.pos] == '?':
|
||||
inc(b.pos)
|
||||
result.callConv = TCallingConvention(decodeVInt(b.s, b.pos))
|
||||
if b.s[b.pos] == '*':
|
||||
inc(b.pos)
|
||||
result.owner = loadSym(g, decodeVInt(b.s, b.pos), info)
|
||||
if b.s[b.pos] == '&':
|
||||
inc(b.pos)
|
||||
result.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
|
||||
if b.s[b.pos] == '/':
|
||||
inc(b.pos)
|
||||
result.size = decodeVInt(b.s, b.pos)
|
||||
else:
|
||||
result.size = -1
|
||||
if b.s[b.pos] == '=':
|
||||
inc(b.pos)
|
||||
result.align = decodeVInt(b.s, b.pos).int16
|
||||
else:
|
||||
result.align = 2
|
||||
|
||||
if b.s[b.pos] == '\14':
|
||||
inc(b.pos)
|
||||
result.lockLevel = decodeVInt(b.s, b.pos).TLockLevel
|
||||
else:
|
||||
result.lockLevel = UnspecifiedLockLevel
|
||||
|
||||
if b.s[b.pos] == '\15':
|
||||
inc(b.pos)
|
||||
result.paddingAtEnd = decodeVInt(b.s, b.pos).int16
|
||||
|
||||
for a in low(result.attachedOps)..high(result.attachedOps):
|
||||
if b.s[b.pos] == '\16':
|
||||
inc(b.pos)
|
||||
let id = decodeVInt(b.s, b.pos)
|
||||
if id >= 0:
|
||||
result.attachedOps[a] = loadSym(g, id, info)
|
||||
|
||||
while b.s[b.pos] == '\19':
|
||||
inc(b.pos)
|
||||
let x = decodeVInt(b.s, b.pos)
|
||||
doAssert b.s[b.pos] == '\20'
|
||||
inc(b.pos)
|
||||
let y = loadSym(g, decodeVInt(b.s, b.pos), info)
|
||||
result.methods.add((x, y))
|
||||
decodeLoc(g, b, result.loc, info)
|
||||
if b.s[b.pos] == '\21':
|
||||
inc(b.pos)
|
||||
let d = decodeVInt(b.s, b.pos)
|
||||
result.typeInst = loadType(g, d, info)
|
||||
while b.s[b.pos] == '^':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == '(':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == ')': inc(b.pos)
|
||||
else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos])
|
||||
rawAddSon(result, nil)
|
||||
else:
|
||||
let d = decodeVInt(b.s, b.pos)
|
||||
result.sons.add loadType(g, d, info)
|
||||
|
||||
proc decodeLib(g; b; info: TLineInfo): PLib =
|
||||
result = nil
|
||||
if b.s[b.pos] == '|':
|
||||
new(result)
|
||||
inc(b.pos)
|
||||
result.kind = TLibKind(decodeVInt(b.s, b.pos))
|
||||
if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1")
|
||||
inc(b.pos)
|
||||
result.name = rope(decodeStr(b.s, b.pos))
|
||||
if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2")
|
||||
inc(b.pos)
|
||||
result.path = decodeNode(g, b, info)
|
||||
|
||||
proc decodeInstantiations(g; b; info: TLineInfo;
|
||||
s: var seq[PInstantiation]) =
|
||||
while b.s[b.pos] == '\15':
|
||||
inc(b.pos)
|
||||
var ii: PInstantiation
|
||||
new ii
|
||||
ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
|
||||
ii.concreteTypes = @[]
|
||||
while b.s[b.pos] == '\17':
|
||||
inc(b.pos)
|
||||
ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info)
|
||||
if b.s[b.pos] == '\20':
|
||||
inc(b.pos)
|
||||
ii.compilesId = decodeVInt(b.s, b.pos)
|
||||
s.add ii
|
||||
|
||||
proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
|
||||
if b.s[b.pos] == '{':
|
||||
inc(b.pos)
|
||||
if b.s[b.pos] == '}':
|
||||
inc(b.pos)
|
||||
return # nil sym
|
||||
var k = TSymKind(decodeVInt(b.s, b.pos))
|
||||
var id: int
|
||||
if b.s[b.pos] == '+':
|
||||
inc(b.pos)
|
||||
id = decodeVInt(b.s, b.pos)
|
||||
setId(id)
|
||||
else:
|
||||
internalError(g.config, info, "decodeSym: no id")
|
||||
var ident: PIdent
|
||||
if b.s[b.pos] == '&':
|
||||
inc(b.pos)
|
||||
ident = g.cache.getIdent(decodeStr(b.s, b.pos))
|
||||
else:
|
||||
internalError(g.config, info, "decodeSym: no ident")
|
||||
#echo "decoding: {", ident.s
|
||||
result = PSym(id: id, kind: k, name: ident)
|
||||
# read the rest of the symbol description:
|
||||
g.incr.r.syms.add(result.id, result)
|
||||
if b.s[b.pos] == '^':
|
||||
inc(b.pos)
|
||||
result.typ = loadType(g, decodeVInt(b.s, b.pos), info)
|
||||
decodeLineInfo(g, b, result.info)
|
||||
if b.s[b.pos] == '*':
|
||||
inc(b.pos)
|
||||
result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
|
||||
if b.s[b.pos] == '$':
|
||||
inc(b.pos)
|
||||
result.flags = cast[TSymFlags](decodeVBiggestInt(b.s, b.pos))
|
||||
if b.s[b.pos] == '@':
|
||||
inc(b.pos)
|
||||
result.magic = TMagic(decodeVInt(b.s, b.pos))
|
||||
if b.s[b.pos] == '!':
|
||||
inc(b.pos)
|
||||
result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos)))
|
||||
if b.s[b.pos] == '%':
|
||||
inc(b.pos)
|
||||
result.position = decodeVInt(b.s, b.pos)
|
||||
if b.s[b.pos] == '`':
|
||||
inc(b.pos)
|
||||
result.offset = decodeVInt(b.s, b.pos)
|
||||
else:
|
||||
result.offset = -1
|
||||
decodeLoc(g, b, result.loc, result.info)
|
||||
result.annex = decodeLib(g, b, info)
|
||||
if b.s[b.pos] == '#':
|
||||
inc(b.pos)
|
||||
result.constraint = decodeNode(g, b, unknownLineInfo)
|
||||
case result.kind
|
||||
of skType, skGenericParam:
|
||||
while b.s[b.pos] == '\14':
|
||||
inc(b.pos)
|
||||
result.typeInstCache.add loadType(g, decodeVInt(b.s, b.pos), result.info)
|
||||
of routineKinds:
|
||||
decodeInstantiations(g, b, result.info, result.procInstCache)
|
||||
if b.s[b.pos] == '\16':
|
||||
inc(b.pos)
|
||||
result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info)
|
||||
if b.s[b.pos] == '\24':
|
||||
inc b.pos
|
||||
result.transformedBody = decodeNode(g, b, result.info)
|
||||
#result.transformedBody = nil
|
||||
of skModule, skPackage:
|
||||
decodeInstantiations(g, b, result.info, result.usedGenerics)
|
||||
of skLet, skVar, skField, skForVar:
|
||||
if b.s[b.pos] == '\18':
|
||||
inc(b.pos)
|
||||
result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info)
|
||||
if b.s[b.pos] == '\19':
|
||||
inc(b.pos)
|
||||
result.bitsize = decodeVInt(b.s, b.pos).int16
|
||||
else: discard
|
||||
|
||||
if b.s[b.pos] == '(':
|
||||
#if result.kind in routineKinds:
|
||||
# result.ast = nil
|
||||
#else:
|
||||
result.ast = decodeNode(g, b, result.info)
|
||||
if sfCompilerProc in result.flags:
|
||||
registerCompilerProc(g, result)
|
||||
#echo "loading ", result.name.s
|
||||
|
||||
proc loadSym(g; id: int; info: TLineInfo): PSym =
|
||||
result = g.incr.r.syms.getOrDefault(id)
|
||||
if result != nil: return result
|
||||
var b = loadBlob(g, sql"select data from syms where nimid = ?", id)
|
||||
result = loadSymFromBlob(g, b, info)
|
||||
doAssert id == result.id, "symbol ID is not consistent!"
|
||||
|
||||
proc registerModule*(g; module: PSym) =
|
||||
g.incr.r.syms.add(abs module.id, module)
|
||||
|
||||
proc loadModuleSymTab(g; module: PSym) =
|
||||
## goal: fill module.tab
|
||||
g.incr.r.syms.add(module.id, module)
|
||||
for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)):
|
||||
let id = parseInt(row[0])
|
||||
var s = g.incr.r.syms.getOrDefault(id)
|
||||
if s == nil:
|
||||
var b = BlobReader(pos: 0)
|
||||
shallowCopy(b.s, row[1])
|
||||
# ensure we can read without index checks:
|
||||
b.s.add '\0'
|
||||
s = loadSymFromBlob(g, b, module.info)
|
||||
assert s != nil
|
||||
if s.kind != skField:
|
||||
strTableAdd(module.tab, s)
|
||||
if sfSystemModule in module.flags:
|
||||
g.systemModule = module
|
||||
|
||||
proc replay(g: ModuleGraph; module: PSym; n: PNode) =
|
||||
# XXX check if we need to replay nkStaticStmt here.
|
||||
case n.kind
|
||||
#of nkStaticStmt:
|
||||
#evalStaticStmt(module, g, n[0], module)
|
||||
#of nkVarSection, nkLetSection:
|
||||
# nkVarSections are already covered by the vmgen which produces nkStaticStmt
|
||||
of nkMethodDef:
|
||||
methodDef(g, n[namePos].sym, fromCache=true)
|
||||
of nkCommentStmt:
|
||||
# pragmas are complex and can be user-overriden via templates. So
|
||||
# instead of using the original ``nkPragma`` nodes, we rely on the
|
||||
# fact that pragmas.nim was patched to produce specialized recorded
|
||||
# statements for us in the form of ``nkCommentStmt`` with (key, value)
|
||||
# pairs. Ordinary nkCommentStmt nodes never have children so this is
|
||||
# not ambiguous.
|
||||
# Fortunately only a tiny subset of the available pragmas need to
|
||||
# be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
|
||||
if n.len >= 2:
|
||||
internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
|
||||
case n[0].strVal
|
||||
of "hint": message(g.config, n.info, hintUser, n[1].strVal)
|
||||
of "warning": message(g.config, n.info, warnUser, n[1].strVal)
|
||||
of "error": localError(g.config, n.info, errUser, n[1].strVal)
|
||||
of "compile":
|
||||
internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit
|
||||
let cname = AbsoluteFile n[1].strVal
|
||||
var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
|
||||
obj: AbsoluteFile n[2].strVal,
|
||||
flags: {CfileFlag.External},
|
||||
customArgs: n[3].strVal)
|
||||
extccomp.addExternalFileToCompile(g.config, cf)
|
||||
of "link":
|
||||
extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
|
||||
of "passl":
|
||||
extccomp.addLinkOption(g.config, n[1].strVal)
|
||||
of "passc":
|
||||
extccomp.addCompileOption(g.config, n[1].strVal)
|
||||
of "localpassc":
|
||||
extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex))
|
||||
of "cppdefine":
|
||||
options.cppDefine(g.config, n[1].strVal)
|
||||
of "inc":
|
||||
let destKey = n[1].strVal
|
||||
let by = n[2].intVal
|
||||
let v = getOrDefault(g.cacheCounters, destKey)
|
||||
g.cacheCounters[destKey] = v+by
|
||||
of "put":
|
||||
let destKey = n[1].strVal
|
||||
let key = n[2].strVal
|
||||
let val = n[3]
|
||||
if not contains(g.cacheTables, destKey):
|
||||
g.cacheTables[destKey] = initBTree[string, PNode]()
|
||||
if not contains(g.cacheTables[destKey], key):
|
||||
g.cacheTables[destKey].add(key, val)
|
||||
else:
|
||||
internalError(g.config, n.info, "key already exists: " & key)
|
||||
of "incl":
|
||||
let destKey = n[1].strVal
|
||||
let val = n[2]
|
||||
if not contains(g.cacheSeqs, destKey):
|
||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
||||
else:
|
||||
block search:
|
||||
for existing in g.cacheSeqs[destKey]:
|
||||
if exprStructuralEquivalent(existing, val, strictSymEquality=true):
|
||||
break search
|
||||
g.cacheSeqs[destKey].add val
|
||||
of "add":
|
||||
let destKey = n[1].strVal
|
||||
let val = n[2]
|
||||
if not contains(g.cacheSeqs, destKey):
|
||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
||||
else:
|
||||
g.cacheSeqs[destKey].add val
|
||||
else:
|
||||
internalAssert g.config, false
|
||||
of nkImportStmt:
|
||||
for x in n:
|
||||
internalAssert g.config, x.kind == nkSym
|
||||
let modpath = AbsoluteFile toFullPath(g.config, x.sym.info)
|
||||
let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, modpath))
|
||||
internalAssert g.config, imported.id < 0
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for x in n: replay(g, module, x)
|
||||
of nkExportStmt:
|
||||
for x in n:
|
||||
doAssert x.kind == nkSym
|
||||
strTableAdd(module.tab, x.sym)
|
||||
else: discard "nothing to do for this node"
|
||||
|
||||
proc loadNode*(g: ModuleGraph; module: PSym): PNode =
|
||||
loadModuleSymTab(g, module)
|
||||
result = newNodeI(nkStmtList, module.info)
|
||||
for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc",
|
||||
abs module.id):
|
||||
var b = BlobReader(pos: 0)
|
||||
# ensure we can read without index checks:
|
||||
b.s = row[0] & '\0'
|
||||
result.add decodeNode(g, b, module.info)
|
||||
db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
|
||||
replay(g, module, result)
|
||||
|
||||
proc setupModuleCache*(g: ModuleGraph) =
|
||||
# historical note: there used to be a `rodfiles` dir with special tests
|
||||
# for incremental compilation via symbol files. This was likely replaced by ic.
|
||||
if g.config.symbolFiles == disabledSf: return
|
||||
g.recordStmt = recordStmt
|
||||
let dbfile = getNimcacheDir(g.config) / RelativeFile"rodfiles.db"
|
||||
if g.config.symbolFiles == writeOnlySf:
|
||||
removeFile(dbfile)
|
||||
createDir getNimcacheDir(g.config)
|
||||
let ec = encodeConfig(g)
|
||||
if not fileExists(dbfile):
|
||||
db = open(connection=string dbfile, user="nim", password="",
|
||||
database="nim")
|
||||
createDb(db)
|
||||
db.exec(sql"insert into config(config) values (?)", ec)
|
||||
else:
|
||||
db = open(connection=string dbfile, user="nim", password="",
|
||||
database="nim")
|
||||
let oldConfig = db.getValue(sql"select config from config")
|
||||
g.incr.configChanged = oldConfig != ec
|
||||
# ensure the filename IDs stay consistent:
|
||||
for row in db.rows(sql"select fullpath, nimid from filenames order by nimid"):
|
||||
let id = fileInfoIdx(g.config, AbsoluteFile row[0])
|
||||
doAssert id.int == parseInt(row[1])
|
||||
db.exec(sql"update config set config = ?", ec)
|
||||
db.exec(sql"pragma journal_mode=off")
|
||||
# This MUST be turned off, otherwise it's way too slow even for testing purposes:
|
||||
db.exec(sql"pragma SYNCHRONOUS=off")
|
||||
db.exec(sql"pragma LOCKING_MODE=exclusive")
|
||||
let lastId = db.getValue(sql"select max(idgen) from controlblock")
|
||||
if lastId.len > 0:
|
||||
idgen.setId(parseInt lastId)
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
## Serialization utilities for the compiler.
|
||||
import strutils, math
|
||||
import std/[strutils, math]
|
||||
|
||||
# bcc on windows doesn't have C99 functions
|
||||
when defined(windows) and defined(bcc):
|
||||
@@ -33,10 +33,19 @@ when defined(windows) and defined(bcc):
|
||||
|
||||
proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
|
||||
|
||||
|
||||
when not declared(signbit):
|
||||
proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "<math.h>".}
|
||||
proc signbit*(x: SomeFloat): bool {.inline.} =
|
||||
result = c_signbit(x) != 0
|
||||
|
||||
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
|
||||
case classify(f)
|
||||
of fcNan:
|
||||
result = "NAN"
|
||||
if signbit(f):
|
||||
result = "-NAN"
|
||||
else:
|
||||
result = "NAN"
|
||||
of fcNegZero:
|
||||
result = "-0.0" & literalPostfix
|
||||
of fcZero:
|
||||
|
||||
@@ -16,10 +16,8 @@ import
|
||||
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||
intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting,
|
||||
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
|
||||
lowerings, plugins/active, rod, lineinfos, strtabs, int128,
|
||||
isolation_check, typeallowed
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
|
||||
lowerings, plugins/active, lineinfos, strtabs, int128,
|
||||
isolation_check, typeallowed, modulegraphs, enumtostr, concepts
|
||||
|
||||
when defined(nimfix):
|
||||
import nimfix/prettybase
|
||||
@@ -38,7 +36,6 @@ proc semProcBody(c: PContext, n: PNode): PNode
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
|
||||
proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
|
||||
|
||||
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
|
||||
proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode
|
||||
proc semOpAux(c: PContext, n: PNode)
|
||||
@@ -83,7 +80,7 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped:
|
||||
changeType(c, x, formal, check=true)
|
||||
result = arg
|
||||
result = skipHiddenSubConv(result, c.idgen)
|
||||
result = skipHiddenSubConv(result, c.graph, c.idgen)
|
||||
|
||||
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
@@ -124,8 +121,8 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
# turn any concrete typedesc into the abstract typedesc type
|
||||
if a.len == 0: result = a
|
||||
else:
|
||||
result = newType(tyTypeDesc, nextId(c.idgen), a.owner)
|
||||
rawAddSon(result, newType(tyNone, nextId(c.idgen), a.owner))
|
||||
result = newType(tyTypeDesc, nextTypeId(c.idgen), a.owner)
|
||||
rawAddSon(result, newType(tyNone, nextTypeId(c.idgen), a.owner))
|
||||
elif b.kind in {tyArray, tySet, tySequence} and
|
||||
a.kind == b.kind:
|
||||
# check for seq[empty] vs. seq[int]
|
||||
@@ -137,7 +134,10 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
let aEmpty = isEmptyContainer(a[i])
|
||||
let bEmpty = isEmptyContainer(b[i])
|
||||
if aEmpty != bEmpty:
|
||||
if nt.isNil: nt = copyType(a, nextId(c.idgen), a.owner)
|
||||
if nt.isNil:
|
||||
nt = copyType(a, nextTypeId(c.idgen), a.owner)
|
||||
copyTypeProps(c.graph, c.idgen.module, nt, a)
|
||||
|
||||
nt[i] = if aEmpty: b[i] else: a[i]
|
||||
if not nt.isNil: result = nt
|
||||
#elif b[idx].kind == tyEmpty: return x
|
||||
@@ -146,7 +146,7 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
# range[0..4]. But then why is (range[0..4], 6) not range[0..6]?
|
||||
# But then why is (2,4) not range[2..4]? But I think this would break
|
||||
# too much code. So ... it's the same range or the base type. This means
|
||||
# type(if b: 0 else 1) == int and not range[0..1]. For now. In the long
|
||||
# typeof(if b: 0 else 1) == int and not range[0..1]. For now. In the long
|
||||
# run people expect ranges to work properly within a tuple.
|
||||
if not sameType(a, b):
|
||||
result = skipTypes(a, {tyRange}).skipIntLit(c.idgen)
|
||||
@@ -176,7 +176,7 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
# ill-formed AST, no need for additional tyRef/tyPtr
|
||||
if k != tyNone and x.kind != tyGenericInst:
|
||||
let r = result
|
||||
result = newType(k, nextId(c.idgen), r.owner)
|
||||
result = newType(k, nextTypeId(c.idgen), r.owner)
|
||||
result.addSonSkipIntLit(r, c.idgen)
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
@@ -193,7 +193,7 @@ proc commonType*(c: PContext; x: PType, y: PNode): PType =
|
||||
commonType(c, x, y.typ)
|
||||
|
||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(c, n), nextId c.idgen, getCurrOwner(c), n.info)
|
||||
result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
|
||||
when defined(nimsuggest):
|
||||
suggestDecl(c, n, result)
|
||||
|
||||
@@ -216,7 +216,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
# template; we must fix it here: see #909
|
||||
result.owner = getCurrOwner(c)
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), nextId c.idgen, getCurrOwner(c), n.info)
|
||||
result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
|
||||
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||
# incl(result.flags, sfGlobal)
|
||||
when defined(nimsuggest):
|
||||
@@ -255,7 +255,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
|
||||
proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
|
||||
if t.sym != nil: return t.sym
|
||||
result = newSym(skType, getIdent(c.cache, "AnonType"), nextId c.idgen, t.owner, info)
|
||||
result = newSym(skType, getIdent(c.cache, "AnonType"), nextSymId c.idgen, t.owner, info)
|
||||
result.flags.incl sfAnon
|
||||
result.typ = t
|
||||
|
||||
@@ -504,6 +504,8 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
|
||||
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
|
||||
var c = newContext(graph, module)
|
||||
c.idgen = idgen
|
||||
c.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil)
|
||||
|
||||
if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
|
||||
c.semConstExpr = semConstExpr
|
||||
c.semExpr = semExpr
|
||||
@@ -559,6 +561,7 @@ proc isEmptyTree(n: PNode): bool =
|
||||
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||
if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
|
||||
if sfSystemModule notin c.module.flags and not isEmptyTree(n):
|
||||
assert c.graph.systemModule != nil
|
||||
c.moduleScope.addSym c.graph.systemModule # import the "System" identifier
|
||||
importAllSymbols(c, c.graph.systemModule)
|
||||
inc c.topStmts
|
||||
@@ -616,7 +619,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} =
|
||||
else:
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
#if c.config.cmd == cmdIdeTools: findSuggest(c, n)
|
||||
rod.storeNode(c.graph, c.module, result)
|
||||
storeRodNode(c, result)
|
||||
|
||||
proc reportUnusedModules(c: PContext) =
|
||||
for i in 0..high(c.unusedImports):
|
||||
@@ -638,7 +641,7 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||
result.add(c.module.ast)
|
||||
popOwner(c)
|
||||
popProcCon(c)
|
||||
storeRemaining(c.graph, c.module)
|
||||
saveRodFile(c)
|
||||
|
||||
const semPass* = makePass(myOpen, myProcess, myClose,
|
||||
isFrontend = true)
|
||||
|
||||
@@ -407,6 +407,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
|
||||
if overloadsState == csEmpty and result.state == csEmpty:
|
||||
if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
|
||||
# xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
|
||||
localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
|
||||
return
|
||||
elif result.state != csMatch:
|
||||
@@ -444,7 +445,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
||||
let a = if a.kind == nkHiddenDeref: a[0] else: a
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
let s = a[0].sym
|
||||
if s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty:
|
||||
if s.isGenericRoutineStrict:
|
||||
let finalCallee = generateInstance(c, s, x.bindings, a.info)
|
||||
a[0].sym = finalCallee
|
||||
a[0].typ = finalCallee.typ
|
||||
@@ -523,7 +524,7 @@ proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
|
||||
return
|
||||
let gp = finalCallee.ast[genericParamsPos]
|
||||
if gp.kind != nkEmpty:
|
||||
if gp.isGenericParams:
|
||||
if x.calleeSym.kind notin {skMacro, skTemplate}:
|
||||
if x.calleeSym.magic in {mArrGet, mArrPut}:
|
||||
finalCallee = x.calleeSym
|
||||
@@ -685,9 +686,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
var x: PType
|
||||
if param.typ.kind == tyVar:
|
||||
x = newTypeS(param.typ.kind, c)
|
||||
x.addSonSkipIntLit(t.baseOfDistinct(c.idgen), c.idgen)
|
||||
x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
|
||||
else:
|
||||
x = t.baseOfDistinct(c.idgen)
|
||||
x = t.baseOfDistinct(c.graph, c.idgen)
|
||||
call.add(newNodeIT(nkEmpty, fn.info, x))
|
||||
if hasDistinct:
|
||||
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
|
||||
|
||||
@@ -9,9 +9,13 @@
|
||||
|
||||
## This module contains the data structures for the semantic checking phase.
|
||||
|
||||
import std / tables
|
||||
|
||||
import
|
||||
intsets, options, ast, astalgo, msgs, idents, renderer,
|
||||
magicsys, vmdef, modulegraphs, lineinfos, sets
|
||||
magicsys, vmdef, modulegraphs, lineinfos, sets, pathutils
|
||||
|
||||
import ic / ic
|
||||
|
||||
type
|
||||
TOptionEntry* = object # entries to put on a stack for pragma parsing
|
||||
@@ -38,6 +42,7 @@ type
|
||||
mappingExists*: bool
|
||||
mapping*: TIdTable
|
||||
caseContext*: seq[tuple[n: PNode, idx: int]]
|
||||
localBindStmts*: seq[PNode]
|
||||
|
||||
TMatchedConcept* = object
|
||||
candidateType*: PType
|
||||
@@ -77,9 +82,9 @@ type
|
||||
case mode*: ImportMode
|
||||
of importAll: discard
|
||||
of importSet:
|
||||
imported*: IntSet
|
||||
imported*: IntSet # of PIdent.id
|
||||
of importExcept:
|
||||
exceptSet*: IntSet
|
||||
exceptSet*: IntSet # of PIdent.id
|
||||
|
||||
PContext* = ref TContext
|
||||
TContext* = object of TPassContext # a context represents the module
|
||||
@@ -91,6 +96,9 @@ type
|
||||
imports*: seq[ImportedModule] # scope for all imported symbols
|
||||
topLevelScope*: PScope # scope for all top-level symbols
|
||||
p*: PProcCon # procedure context
|
||||
intTypeCache*: array[-5..32, PType] # cache some common integer types
|
||||
# to avoid type allocations
|
||||
nilTypeCache*: PType
|
||||
matchedConcept*: ptr TMatchedConcept # the current concept being matched
|
||||
friendModules*: seq[PSym] # friend modules; may access private data;
|
||||
# this is used so that generic instantiations
|
||||
@@ -145,18 +153,57 @@ type
|
||||
suggestionsMade*: bool
|
||||
isAmbiguous*: bool # little hack
|
||||
features*: set[Feature]
|
||||
inTypeContext*: int
|
||||
typesWithOps*: seq[(PType, PType)] #\
|
||||
# We need to instantiate the type bound ops lazily after
|
||||
# the generic type has been constructed completely. See
|
||||
# tests/destructor/topttree.nim for an example that
|
||||
# would otherwise fail.
|
||||
inTypeContext*, inConceptDecl*: int
|
||||
unusedImports*: seq[(PSym, TLineInfo)]
|
||||
exportIndirections*: HashSet[(int, int)]
|
||||
lastTLineInfo*: TLineInfo
|
||||
|
||||
template config*(c: PContext): ConfigRef = c.graph.config
|
||||
|
||||
proc getIntLitType*(c: PContext; literal: PNode): PType =
|
||||
# we cache some common integer literal types for performance:
|
||||
let value = literal.intVal
|
||||
if value >= low(c.intTypeCache) and value <= high(c.intTypeCache):
|
||||
result = c.intTypeCache[value.int]
|
||||
if result == nil:
|
||||
let ti = getSysType(c.graph, literal.info, tyInt)
|
||||
result = copyType(ti, nextTypeId(c.idgen), ti.owner)
|
||||
result.n = literal
|
||||
c.intTypeCache[value.int] = result
|
||||
else:
|
||||
let ti = getSysType(c.graph, literal.info, tyInt)
|
||||
result = copyType(ti, nextTypeId(c.idgen), ti.owner)
|
||||
result.n = literal
|
||||
|
||||
proc setIntLitType*(c: PContext; result: PNode) =
|
||||
let i = result.intVal
|
||||
case c.config.target.intSize
|
||||
of 8: result.typ = getIntLitType(c, result)
|
||||
of 4:
|
||||
if i >= low(int32) and i <= high(int32):
|
||||
result.typ = getIntLitType(c, result)
|
||||
else:
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
of 2:
|
||||
if i >= low(int16) and i <= high(int16):
|
||||
result.typ = getIntLitType(c, result)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ = getSysType(c.graph, result.info, tyInt32)
|
||||
else:
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
of 1:
|
||||
# 8 bit CPUs are insane ...
|
||||
if i >= low(int8) and i <= high(int8):
|
||||
result.typ = getIntLitType(c, result)
|
||||
elif i >= low(int16) and i <= high(int16):
|
||||
result.typ = getSysType(c.graph, result.info, tyInt16)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ = getSysType(c.graph, result.info, tyInt32)
|
||||
else:
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
else:
|
||||
internalError(c.config, result.info, "invalid int size")
|
||||
|
||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||
result.genericSym = s
|
||||
result.inst = inst
|
||||
@@ -247,7 +294,6 @@ proc popOptionEntry*(c: PContext) =
|
||||
|
||||
proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
||||
new(result)
|
||||
result.enforceVoidContext = PType(kind: tyTyped)
|
||||
result.optionStack = @[newOptionEntry(graph.config)]
|
||||
result.libs = @[]
|
||||
result.module = module
|
||||
@@ -262,24 +308,64 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
||||
result.cache = graph.cache
|
||||
result.graph = graph
|
||||
initStrTable(result.signatures)
|
||||
result.typesWithOps = @[]
|
||||
result.features = graph.config.features
|
||||
if graph.config.symbolFiles != disabledSf:
|
||||
let id = module.position
|
||||
assert graph.packed[id].status in {undefined, outdated}
|
||||
graph.packed[id].status = storing
|
||||
graph.packed[id].module = module
|
||||
initEncoder graph, module
|
||||
|
||||
proc inclSym(sq: var seq[PSym], s: PSym) =
|
||||
template packedRepr*(c): untyped = c.graph.packed[c.module.position].fromDisk
|
||||
template encoder*(c): untyped = c.graph.encoders[c.module.position]
|
||||
|
||||
proc addIncludeFileDep*(c: PContext; f: FileIndex) =
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addIncludeFileDep(c.encoder, c.packedRepr, f)
|
||||
|
||||
proc addImportFileDep*(c: PContext; f: FileIndex) =
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addImportFileDep(c.encoder, c.packedRepr, f)
|
||||
|
||||
proc addPragmaComputation*(c: PContext; n: PNode) =
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addPragmaComputation(c.encoder, c.packedRepr, n)
|
||||
|
||||
proc inclSym(sq: var seq[PSym], s: PSym): bool =
|
||||
for i in 0..<sq.len:
|
||||
if sq[i].id == s.id: return
|
||||
if sq[i].id == s.id: return false
|
||||
sq.add s
|
||||
result = true
|
||||
|
||||
proc addConverter*(c: PContext, conv: PSym) =
|
||||
inclSym(c.converters, conv)
|
||||
inclSym(c.graph.ifaces[c.module.position].converters, conv)
|
||||
proc addConverter*(c: PContext, conv: LazySym) =
|
||||
assert conv.sym != nil
|
||||
if inclSym(c.converters, conv.sym):
|
||||
add(c.graph.ifaces[c.module.position].converters, conv)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addConverter(c.encoder, c.packedRepr, conv.sym)
|
||||
|
||||
proc addPureEnum*(c: PContext, e: PSym) =
|
||||
inclSym(c.graph.ifaces[c.module.position].pureEnums, e)
|
||||
proc addPureEnum*(c: PContext, e: LazySym) =
|
||||
assert e.sym != nil
|
||||
add(c.graph.ifaces[c.module.position].pureEnums, e)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addPureEnum(c.encoder, c.packedRepr, e.sym)
|
||||
|
||||
proc addPattern*(c: PContext, p: PSym) =
|
||||
inclSym(c.patterns, p)
|
||||
inclSym(c.graph.ifaces[c.module.position].patterns, p)
|
||||
proc addPattern*(c: PContext, p: LazySym) =
|
||||
assert p.sym != nil
|
||||
if inclSym(c.patterns, p.sym):
|
||||
add(c.graph.ifaces[c.module.position].patterns, p)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addTrmacro(c.encoder, c.packedRepr, p.sym)
|
||||
|
||||
proc exportSym*(c: PContext; s: PSym) =
|
||||
strTableAdd(c.module.semtab(c.graph), s)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addExported(c.encoder, c.packedRepr, s)
|
||||
|
||||
proc reexportSym*(c: PContext; s: PSym) =
|
||||
strTableAdd(c.module.semtab(c.graph), s)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addReexport(c.encoder, c.packedRepr, s)
|
||||
|
||||
proc newLib*(kind: TLibKind): PLib =
|
||||
new(result)
|
||||
@@ -291,10 +377,10 @@ proc addToLib*(lib: PLib, sym: PSym) =
|
||||
sym.annex = lib
|
||||
|
||||
proc newTypeS*(kind: TTypeKind, c: PContext): PType =
|
||||
result = newType(kind, nextId(c.idgen), getCurrOwner(c))
|
||||
result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c))
|
||||
|
||||
proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType =
|
||||
result = newType(tyPtr, nextId(idgen), owner)
|
||||
result = newType(tyPtr, nextTypeId(idgen), owner)
|
||||
addSonSkipIntLit(result, baseType, idgen)
|
||||
|
||||
proc makePtrType*(c: PContext, baseType: PType): PType =
|
||||
@@ -322,25 +408,17 @@ proc makeVarType*(owner: PSym, baseType: PType; idgen: IdGenerator; kind = tyVar
|
||||
if baseType.kind == kind:
|
||||
result = baseType
|
||||
else:
|
||||
result = newType(kind, nextId(idgen), owner)
|
||||
result = newType(kind, nextTypeId(idgen), owner)
|
||||
addSonSkipIntLit(result, baseType, idgen)
|
||||
|
||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
if typ.kind == tyTypeDesc:
|
||||
result = typ
|
||||
else:
|
||||
result = newTypeS(tyTypeDesc, c)
|
||||
incl result.flags, tfCheckedForDestructor
|
||||
result.addSonSkipIntLit(typ, c.idgen)
|
||||
|
||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||
let typedesc = newTypeS(tyTypeDesc, c)
|
||||
incl typedesc.flags, tfCheckedForDestructor
|
||||
internalAssert(c.config, typ != nil)
|
||||
typedesc.addSonSkipIntLit(typ, c.idgen)
|
||||
let sym = newSym(skType, c.cache.idAnon, nextId(c.idgen), getCurrOwner(c), info,
|
||||
let sym = newSym(skType, c.cache.idAnon, nextSymId(c.idgen), getCurrOwner(c), info,
|
||||
c.config.options).linkTo(typedesc)
|
||||
return newSymNode(sym, info)
|
||||
result = newSymNode(sym, info)
|
||||
|
||||
proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
|
||||
result = newTypeS(tyFromExpr, c)
|
||||
@@ -349,12 +427,12 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
|
||||
|
||||
proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType];
|
||||
idgen: IdGenerator): PType =
|
||||
result = newType(kind, nextId(idgen), owner)
|
||||
result = newType(kind, nextTypeId(idgen), owner)
|
||||
result.sons = sons
|
||||
|
||||
proc newTypeWithSons*(c: PContext, kind: TTypeKind,
|
||||
sons: seq[PType]): PType =
|
||||
result = newType(kind, nextId(c.idgen), getCurrOwner(c))
|
||||
result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c))
|
||||
result.sons = sons
|
||||
|
||||
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
|
||||
@@ -463,3 +541,42 @@ proc popCaseContext*(c: PContext) =
|
||||
|
||||
proc setCaseContextIdx*(c: PContext, idx: int) =
|
||||
c.p.caseContext[^1].idx = idx
|
||||
|
||||
template addExport*(c: PContext; s: PSym) =
|
||||
## convenience to export a symbol from the current module
|
||||
addExport(c.graph, c.module, s)
|
||||
|
||||
proc storeRodNode*(c: PContext, n: PNode) =
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
toPackedNodeTopLevel(n, c.encoder, c.packedRepr)
|
||||
|
||||
proc addToGenericProcCache*(c: PContext; s: PSym; inst: PInstantiation) =
|
||||
c.graph.procInstCache.mgetOrPut(s.itemId, @[]).add LazyInstantiation(module: c.module.position, inst: inst)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
storeInstantiation(c.encoder, c.packedRepr, s, inst)
|
||||
|
||||
proc addToGenericCache*(c: PContext; s: PSym; inst: PType) =
|
||||
c.graph.typeInstCache.mgetOrPut(s.itemId, @[]).add LazyType(typ: inst)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
storeTypeInst(c.encoder, c.packedRepr, s, inst)
|
||||
|
||||
proc saveRodFile*(c: PContext) =
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
if c.graph.vm != nil:
|
||||
for (m, n) in PCtx(c.graph.vm).vmstateDiff:
|
||||
if m == c.module:
|
||||
addPragmaComputation(c, n)
|
||||
if sfSystemModule in c.module.flags:
|
||||
c.graph.systemModuleComplete = true
|
||||
c.idgen.sealed = true # no further additions are allowed
|
||||
if c.config.symbolFiles != stressTest:
|
||||
# For stress testing we seek to reload the symbols from memory. This
|
||||
# way much of the logic is tested but the test is reproducible as it does
|
||||
# not depend on the hard disk contents!
|
||||
saveRodFile(toRodFile(c.config, AbsoluteFile toFullPath(c.config, FileIndex c.module.position)),
|
||||
c.encoder, c.packedRepr)
|
||||
else:
|
||||
# debug code, but maybe a good idea for production? Could reduce the compiler's
|
||||
# memory consumption considerably at the cost of more loads from disk.
|
||||
simulateCachedModule(c.graph, c.module, c.packedRepr)
|
||||
c.graph.packed[c.module.position].status = loaded
|
||||
|
||||
@@ -66,10 +66,17 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
rejectEmptyNode(n)
|
||||
result = semExpr(c, n, flags+{efWantValue})
|
||||
if result.kind == nkEmpty:
|
||||
|
||||
let
|
||||
isEmpty = result.kind == nkEmpty
|
||||
isTypeError = result.typ != nil and result.typ.kind == tyError
|
||||
|
||||
if isEmpty or isTypeError:
|
||||
# bug #12741, redundant error messages are the lesser evil here:
|
||||
localError(c.config, n.info, errExprXHasNoType %
|
||||
renderTree(result, {renderNoComments}))
|
||||
|
||||
if isEmpty:
|
||||
# do not produce another redundant error message:
|
||||
result = errorNode(c, n)
|
||||
|
||||
@@ -79,6 +86,9 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
localError(c.config, n.info, errExprXHasNoType %
|
||||
renderTree(result, {renderNoComments}))
|
||||
result.typ = errorType(c)
|
||||
elif result.typ.kind == tyError:
|
||||
# associates the type error to the current owner
|
||||
result.typ = errorType(c)
|
||||
else:
|
||||
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
|
||||
|
||||
@@ -408,8 +418,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
of "closure":
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
res = t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
tfIterator notin t.flags
|
||||
t.callConv == ccClosure
|
||||
of "iterator":
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
res = t.kind == tyProc and
|
||||
@@ -843,7 +852,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||
# consider: 'for x in pReturningArray()' --> we don't want the restriction
|
||||
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
|
||||
# for typeof support.
|
||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||
# for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
|
||||
result = semOverloadedCall(c, n, nOrig,
|
||||
{skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
|
||||
else:
|
||||
@@ -888,6 +897,9 @@ proc setGenericParams(c: PContext, n: PNode) =
|
||||
n[i].typ = semTypeNode(c, n[i], nil)
|
||||
|
||||
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
|
||||
return errorNode(c, n)
|
||||
|
||||
result = n
|
||||
let callee = result[0].sym
|
||||
case callee.kind
|
||||
@@ -1015,7 +1027,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
proc buildEchoStmt(c: PContext, n: PNode): PNode =
|
||||
# we MUST not check 'n' for semantics again here! But for now we give up:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo"))
|
||||
let e = systemModuleSym(c.graph, getIdent(c.cache, "echo"))
|
||||
if e != nil:
|
||||
result.add(newSymNode(e))
|
||||
else:
|
||||
@@ -1073,7 +1085,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
||||
s = newNodeIT(nkCurly, n.info, setType)
|
||||
for j in 0..<it.len - 1: s.add copyTree(it[j])
|
||||
var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
|
||||
inExpr.add newSymNode(c.graph.opContains, n.info)
|
||||
inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info)
|
||||
inExpr.add s
|
||||
inExpr.add copyTree(r[0])
|
||||
check.add inExpr
|
||||
@@ -1086,11 +1098,11 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
||||
check = newNodeI(nkCheckedFieldExpr, n.info)
|
||||
check.add c.graph.emptyNode # make space for access node
|
||||
var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
|
||||
inExpr.add newSymNode(c.graph.opContains, n.info)
|
||||
inExpr.add newSymNode(getSysMagic(c.graph, n.info, "contains", mInSet), n.info)
|
||||
inExpr.add s
|
||||
inExpr.add copyTree(r[0])
|
||||
var notExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
|
||||
notExpr.add newSymNode(c.graph.opNot, n.info)
|
||||
notExpr.add newSymNode(getSysMagic(c.graph, n.info, "not", mNot), n.info)
|
||||
notExpr.add inExpr
|
||||
check.add notExpr
|
||||
return
|
||||
@@ -1119,7 +1131,7 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
# This seems semantically correct and then we'll be able
|
||||
# to return the section symbol directly here
|
||||
let foundType = makeTypeDesc(c, def[2].typ)
|
||||
return newSymNode(copySym(def[0].sym, nextId c.idgen).linkTo(foundType), info)
|
||||
return newSymNode(copySym(def[0].sym, nextSymId c.idgen).linkTo(foundType), info)
|
||||
|
||||
of nkConstSection:
|
||||
for def in statement:
|
||||
@@ -1144,7 +1156,7 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
return c.graph.emptyNode
|
||||
else:
|
||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||
return newSymNode(copySym(tParam.sym, nextId c.idgen).linkTo(foundTyp), info)
|
||||
return newSymNode(copySym(tParam.sym, nextSymId c.idgen).linkTo(foundTyp), info)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1369,6 +1381,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
return tryReadingTypeField(c, n, i, ty.base)
|
||||
elif isTypeExpr(n.sons[0]):
|
||||
return tryReadingTypeField(c, n, i, ty)
|
||||
elif ty.kind == tyError:
|
||||
# a type error doesn't have any builtin fields
|
||||
return nil
|
||||
|
||||
if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
|
||||
ty = ty.lastSon
|
||||
@@ -1898,7 +1913,7 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
|
||||
if m == c.module:
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
else:
|
||||
result = strTableGet(m.tab, ident)
|
||||
result = someSym(c.graph, m, ident)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
@@ -1941,7 +1956,7 @@ proc expectString(c: PContext, n: PNode): string =
|
||||
localError(c.config, n.info, errStringLiteralExpected)
|
||||
|
||||
proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
|
||||
result = newSym(kind, c.cache.idAnon, nextId c.idgen, getCurrOwner(c), info)
|
||||
result = newSym(kind, c.cache.idAnon, nextSymId c.idgen, getCurrOwner(c), info)
|
||||
|
||||
proc semExpandToAst(c: PContext, n: PNode): PNode =
|
||||
let macroCall = n[1]
|
||||
@@ -2007,17 +2022,27 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
|
||||
ids.add n
|
||||
return
|
||||
|
||||
template handlePrefixOp(prefixed) =
|
||||
if prefixed[0].kind == nkIdent:
|
||||
let examinedOp = prefixed[0].ident.s
|
||||
if examinedOp == op:
|
||||
returnQuote prefixed[1]
|
||||
elif examinedOp.startsWith(op):
|
||||
prefixed[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), prefixed.info)
|
||||
|
||||
if n.kind == nkPrefix:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
if n[0].kind == nkIdent:
|
||||
var examinedOp = n[0].ident.s
|
||||
if examinedOp == op:
|
||||
returnQuote n[1]
|
||||
elif examinedOp.startsWith(op):
|
||||
n[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info)
|
||||
elif n.kind == nkAccQuoted and op == "``":
|
||||
returnQuote n[0]
|
||||
handlePrefixOp(n)
|
||||
elif n.kind == nkAccQuoted:
|
||||
if op == "``":
|
||||
returnQuote n[0]
|
||||
else: # [bug #7589](https://github.com/nim-lang/Nim/issues/7589)
|
||||
if n.len == 2 and n[0].ident.s == op:
|
||||
var tempNode = nkPrefix.newTree()
|
||||
tempNode.newSons(2)
|
||||
tempNode[0] = n[0]
|
||||
tempNode[1] = n[1]
|
||||
handlePrefixOp(tempNode)
|
||||
elif n.kind == nkIdent:
|
||||
if n.ident.s == "result":
|
||||
n = ids[0]
|
||||
@@ -2074,7 +2099,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||
identNodeSym.newSymNode
|
||||
quotes[1] = newTreeI(nkCall, n.info, identNode, newStrNode(nkStrLit, "result"))
|
||||
result = newTreeI(nkCall, n.info,
|
||||
createMagic(c.graph, "getAst", mExpandToAst).newSymNode,
|
||||
createMagic(c.graph, c.idgen, "getAst", mExpandToAst).newSymNode,
|
||||
newTreeI(nkCall, n.info, quotes))
|
||||
result = semExpandToAst(c, result)
|
||||
|
||||
@@ -2156,7 +2181,7 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result = semDirectOp(c, n, flags)
|
||||
|
||||
proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
|
||||
result = newType(tyGenericInvocation, nextId c.idgen, c.module)
|
||||
result = newType(tyGenericInvocation, nextTypeId c.idgen, c.module)
|
||||
addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ, c.idgen)
|
||||
addSonSkipIntLit(result, t, c.idgen)
|
||||
result = instGenericContainer(c, info, result, allowMetaTypes = false)
|
||||
@@ -2349,6 +2374,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
discard
|
||||
elif e.intVal != 0 and result == nil:
|
||||
setResult(it[1])
|
||||
return # we're not in nimvm and we already have a result
|
||||
of nkElse, nkElseExpr:
|
||||
checkSonsLen(it, 1, c.config)
|
||||
if result == nil or whenNimvm:
|
||||
@@ -2505,7 +2531,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
elif labl.owner == nil:
|
||||
labl.owner = c.p.owner
|
||||
n[0] = newSymNode(labl, n[0].info)
|
||||
suggestSym(c.config, n[0].info, labl, c.graph.usageSym)
|
||||
suggestSym(c.graph, n[0].info, labl, c.graph.usageSym)
|
||||
styleCheckDef(c.config, labl)
|
||||
onDef(n[0].info, labl)
|
||||
n[1] = semExpr(c, n[1], flags)
|
||||
@@ -2523,22 +2549,19 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
|
||||
let exceptSet = readExceptSet(c, n)
|
||||
let exported = moduleName.sym
|
||||
result = newNodeI(nkExportStmt, n.info)
|
||||
strTableAdd(c.module.tab, exported)
|
||||
var i: TTabIter
|
||||
var s = initTabIter(i, exported.tab)
|
||||
while s != nil:
|
||||
reexportSym(c, exported)
|
||||
for s in allSyms(c.graph, exported):
|
||||
if s.kind in ExportableSymKinds+{skModule} and
|
||||
s.name.id notin exceptSet and sfError notin s.flags:
|
||||
strTableAdd(c.module.tab, s)
|
||||
reexportSym(c, s)
|
||||
result.add newSymNode(s, n.info)
|
||||
s = nextIter(i, exported.tab)
|
||||
markUsed(c, n.info, exported)
|
||||
|
||||
proc semExport(c: PContext, n: PNode): PNode =
|
||||
proc specialSyms(c: PContext; s: PSym) {.inline.} =
|
||||
if s.kind == skConverter: addConverter(c, s)
|
||||
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
|
||||
elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags:
|
||||
addPureEnum(c, s)
|
||||
addPureEnum(c, LazySym(sym: s))
|
||||
|
||||
result = newNodeI(nkExportStmt, n.info)
|
||||
for i in 0..<n.len:
|
||||
@@ -2549,15 +2572,12 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
|
||||
elif s.kind == skModule:
|
||||
# forward everything from that module:
|
||||
strTableAdd(c.module.tab, s)
|
||||
var ti: TTabIter
|
||||
var it = initTabIter(ti, s.tab)
|
||||
while it != nil:
|
||||
reexportSym(c, s)
|
||||
for it in allSyms(c.graph, s):
|
||||
if it.kind in ExportableSymKinds+{skModule}:
|
||||
strTableAdd(c.module.tab, it)
|
||||
reexportSym(c, it)
|
||||
result.add newSymNode(it, a.info)
|
||||
specialSyms(c, it)
|
||||
it = nextIter(ti, s.tab)
|
||||
markUsed(c, n.info, s)
|
||||
else:
|
||||
while s != nil:
|
||||
@@ -2566,7 +2586,7 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
"; enum field cannot be exported individually")
|
||||
if s.kind in ExportableSymKinds+{skModule} and sfError notin s.flags:
|
||||
result.add(newSymNode(s, a.info))
|
||||
strTableAdd(c.module.tab, s)
|
||||
reexportSym(c, s)
|
||||
markUsed(c, n.info, s)
|
||||
specialSyms(c, s)
|
||||
if s.kind == skType and sfPure notin s.flags:
|
||||
@@ -2576,7 +2596,7 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
var e = etyp.n[j].sym
|
||||
if e.kind != skEnumField:
|
||||
internalError(c.config, s.info, "rawImportSymbol")
|
||||
strTableAdd(c.module.tab, e)
|
||||
reexportSym(c, e)
|
||||
|
||||
s = nextOverloadIter(o, c, a)
|
||||
|
||||
@@ -2631,7 +2651,7 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
|
||||
let paramPos = defExpr.sym.position + 1
|
||||
|
||||
if call[paramPos].kind != nkSym:
|
||||
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), nextId c.idgen,
|
||||
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), nextSymId c.idgen,
|
||||
c.p.owner, letSection.info, c.p.owner.options)
|
||||
hoistedVarSym.typ = call[paramPos].typ
|
||||
|
||||
@@ -2648,6 +2668,14 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
|
||||
for i in 0..<defExpr.safeLen:
|
||||
hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
|
||||
|
||||
proc getNilType(c: PContext): PType =
|
||||
result = c.nilTypeCache
|
||||
if result == nil:
|
||||
result = newTypeS(tyNil, c)
|
||||
result.size = c.config.target.ptrSize
|
||||
result.align = c.config.target.ptrSize.int16
|
||||
c.nilTypeCache = result
|
||||
|
||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
when defined(nimCompilerStackraceHints):
|
||||
setFrameMsg c.config$n.info & " " & $n.kind
|
||||
@@ -2683,9 +2711,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkEmpty, nkNone, nkCommentStmt, nkType:
|
||||
discard
|
||||
of nkNilLit:
|
||||
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyNil)
|
||||
if result.typ == nil: result.typ = getNilType(c)
|
||||
of nkIntLit:
|
||||
if result.typ == nil: setIntLitType(c.graph, result)
|
||||
if result.typ == nil: setIntLitType(c, result)
|
||||
of nkInt8Lit:
|
||||
if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyInt8)
|
||||
of nkInt16Lit:
|
||||
@@ -2828,7 +2856,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
of nkCurly: result = semSetConstr(c, n)
|
||||
of nkBracket: result = semArrayConstr(c, n, flags)
|
||||
of nkObjConstr: result = semObjConstr(c, n, flags)
|
||||
of nkLambdaKinds: result = semLambda(c, n, flags)
|
||||
of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
|
||||
of nkDerefExpr: result = semDeref(c, n)
|
||||
of nkAddr:
|
||||
result = n
|
||||
@@ -2926,6 +2954,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
for i in 0..<n.len:
|
||||
n[i] = semExpr(c, n[i])
|
||||
of nkComesFrom: discard "ignore the comes from information for now"
|
||||
of nkMixinStmt: discard
|
||||
of nkBindStmt:
|
||||
if c.p != nil:
|
||||
c.p.localBindStmts.add n
|
||||
else:
|
||||
localError(c.config, n.info, "invalid context for 'bind' statement: " &
|
||||
renderTree(n, {renderNoComments}))
|
||||
else:
|
||||
localError(c.config, n.info, "invalid expression: " &
|
||||
renderTree(n, {renderNoComments}))
|
||||
|
||||
@@ -106,10 +106,10 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
# so that 'break' etc. work as expected, we produce
|
||||
# a 'while true: stmt; break' loop ...
|
||||
result = newNodeI(nkWhileStmt, n.info, 2)
|
||||
var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
|
||||
var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
|
||||
if trueSymbol == nil:
|
||||
localError(c.config, n.info, "system needs: 'true'")
|
||||
trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextId c.idgen, getCurrOwner(c), n.info)
|
||||
trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info)
|
||||
trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
|
||||
|
||||
result[0] = newSymNode(trueSymbol, n.info)
|
||||
|
||||
@@ -15,18 +15,26 @@ import
|
||||
platform, math, msgs, idents, renderer, types,
|
||||
commands, magicsys, modulegraphs, strtabs, lineinfos
|
||||
|
||||
from system/memory import nimCStrLen
|
||||
|
||||
proc errorType*(g: ModuleGraph): PType =
|
||||
## creates a type representing an error state
|
||||
result = newType(tyError, nextId(g.idgen), g.owners[^1])
|
||||
result = newType(tyError, nextTypeId(g.idgen), g.owners[^1])
|
||||
result.flags.incl tfCheckedForDestructor
|
||||
|
||||
proc newIntNodeT*(intVal: Int128, n: PNode; g: ModuleGraph): PNode =
|
||||
proc getIntLitTypeG(g: ModuleGraph; literal: PNode; idgen: IdGenerator): PType =
|
||||
# we cache some common integer literal types for performance:
|
||||
let ti = getSysType(g, literal.info, tyInt)
|
||||
result = copyType(ti, nextTypeId(idgen), ti.owner)
|
||||
result.n = literal
|
||||
|
||||
proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
result = newIntTypeNode(intVal, n.typ)
|
||||
# See bug #6989. 'pred' et al only produce an int literal type if the
|
||||
# original type was 'int', not a distinct int etc.
|
||||
if n.typ.kind == tyInt:
|
||||
# access cache for the int lit type
|
||||
result.typ = getIntLitType(g, result)
|
||||
result.typ = getIntLitTypeG(g, result, idgen)
|
||||
result.info = n.info
|
||||
|
||||
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
|
||||
@@ -45,33 +53,33 @@ proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
|
||||
proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
# evaluates the constant expression or returns nil if it is no constant
|
||||
# expression
|
||||
proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode
|
||||
proc evalOp*(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
|
||||
proc checkInRange(conf: ConfigRef; n: PNode, res: Int128): bool =
|
||||
res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ)
|
||||
|
||||
proc foldAdd(a, b: Int128, n: PNode; g: ModuleGraph): PNode =
|
||||
proc foldAdd(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
let res = a + b
|
||||
if checkInRange(g.config, n, res):
|
||||
result = newIntNodeT(res, n, g)
|
||||
result = newIntNodeT(res, n, idgen, g)
|
||||
|
||||
proc foldSub(a, b: Int128, n: PNode; g: ModuleGraph): PNode =
|
||||
proc foldSub(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
let res = a - b
|
||||
if checkInRange(g.config, n, res):
|
||||
result = newIntNodeT(res, n, g)
|
||||
result = newIntNodeT(res, n, idgen, g)
|
||||
|
||||
proc foldUnarySub(a: Int128, n: PNode, g: ModuleGraph): PNode =
|
||||
proc foldUnarySub(a: Int128, n: PNode; idgen: IdGenerator, g: ModuleGraph): PNode =
|
||||
if a != firstOrd(g.config, n.typ):
|
||||
result = newIntNodeT(-a, n, g)
|
||||
result = newIntNodeT(-a, n, idgen, g)
|
||||
|
||||
proc foldAbs(a: Int128, n: PNode; g: ModuleGraph): PNode =
|
||||
proc foldAbs(a: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
if a != firstOrd(g.config, n.typ):
|
||||
result = newIntNodeT(abs(a), n, g)
|
||||
result = newIntNodeT(abs(a), n, idgen, g)
|
||||
|
||||
proc foldMul(a, b: Int128, n: PNode; g: ModuleGraph): PNode =
|
||||
proc foldMul(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
let res = a * b
|
||||
if checkInRange(g.config, n, res):
|
||||
return newIntNodeT(res, n, g)
|
||||
return newIntNodeT(res, n, idgen, g)
|
||||
|
||||
proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
|
||||
# because $ has the param ordinal[T], `a` is not necessarily an enum, but an
|
||||
@@ -113,65 +121,68 @@ proc pickIntRange(a, b: PType): PType =
|
||||
proc isIntRangeOrLit(t: PType): bool =
|
||||
result = isIntRange(t) or isIntLit(t)
|
||||
|
||||
proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
# b and c may be nil
|
||||
result = nil
|
||||
case m
|
||||
of mOrd: result = newIntNodeT(getOrdValue(a), n, g)
|
||||
of mChr: result = newIntNodeT(getInt(a), n, g)
|
||||
of mUnaryMinusI, mUnaryMinusI64: result = foldUnarySub(getInt(a), n, g)
|
||||
of mOrd: result = newIntNodeT(getOrdValue(a), n, idgen, g)
|
||||
of mChr: result = newIntNodeT(getInt(a), n, idgen, g)
|
||||
of mUnaryMinusI, mUnaryMinusI64: result = foldUnarySub(getInt(a), n, idgen, g)
|
||||
of mUnaryMinusF64: result = newFloatNodeT(-getFloat(a), n, g)
|
||||
of mNot: result = newIntNodeT(One - getInt(a), n, g)
|
||||
of mCard: result = newIntNodeT(toInt128(nimsets.cardSet(g.config, a)), n, g)
|
||||
of mNot: result = newIntNodeT(One - getInt(a), n, idgen, g)
|
||||
of mCard: result = newIntNodeT(toInt128(nimsets.cardSet(g.config, a)), n, idgen, g)
|
||||
of mBitnotI:
|
||||
if n.typ.isUnsigned:
|
||||
result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(n.typ.size)), n, g)
|
||||
result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(n.typ.size)), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(bitnot(getInt(a)), n, g)
|
||||
of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g)
|
||||
result = newIntNodeT(bitnot(getInt(a)), n, idgen, g)
|
||||
of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, idgen, g)
|
||||
of mLengthSeq, mLengthOpenArray, mLengthStr:
|
||||
if a.kind == nkNilLit:
|
||||
result = newIntNodeT(Zero, n, g)
|
||||
result = newIntNodeT(Zero, n, idgen, g)
|
||||
elif a.kind in {nkStrLit..nkTripleStrLit}:
|
||||
result = newIntNodeT(toInt128(a.strVal.len), n, g)
|
||||
if a.typ.kind == tyString:
|
||||
result = newIntNodeT(toInt128(a.strVal.len), n, idgen, g)
|
||||
elif a.typ.kind == tyCString:
|
||||
result = newIntNodeT(toInt128(nimCStrLen(a.strVal)), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(a.len), n, g)
|
||||
result = newIntNodeT(toInt128(a.len), n, idgen, g)
|
||||
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
|
||||
# XXX: Hides overflow/underflow
|
||||
of mAbsI: result = foldAbs(getInt(a), n, g)
|
||||
of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, g)
|
||||
of mPred: result = foldSub(getOrdValue(a), getInt(b), n, g)
|
||||
of mAddI: result = foldAdd(getInt(a), getInt(b), n, g)
|
||||
of mSubI: result = foldSub(getInt(a), getInt(b), n, g)
|
||||
of mMulI: result = foldMul(getInt(a), getInt(b), n, g)
|
||||
of mAbsI: result = foldAbs(getInt(a), n, idgen, g)
|
||||
of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, idgen, g)
|
||||
of mPred: result = foldSub(getOrdValue(a), getInt(b), n, idgen, g)
|
||||
of mAddI: result = foldAdd(getInt(a), getInt(b), n, idgen, g)
|
||||
of mSubI: result = foldSub(getInt(a), getInt(b), n, idgen, g)
|
||||
of mMulI: result = foldMul(getInt(a), getInt(b), n, idgen, g)
|
||||
of mMinI:
|
||||
let argA = getInt(a)
|
||||
let argB = getInt(b)
|
||||
result = newIntNodeT(if argA < argB: argA else: argB, n, g)
|
||||
result = newIntNodeT(if argA < argB: argA else: argB, n, idgen, g)
|
||||
of mMaxI:
|
||||
let argA = getInt(a)
|
||||
let argB = getInt(b)
|
||||
result = newIntNodeT(if argA > argB: argA else: argB, n, g)
|
||||
result = newIntNodeT(if argA > argB: argA else: argB, n, idgen, g)
|
||||
of mShlI:
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, g)
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for shl")
|
||||
of mShrI:
|
||||
var a = cast[uint64](getInt(a))
|
||||
@@ -192,75 +203,75 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
# unsigned and 64 bit integers don't need masking
|
||||
discard
|
||||
let c = cast[BiggestInt](a shr b)
|
||||
result = newIntNodeT(toInt128(c), n, g)
|
||||
result = newIntNodeT(toInt128(c), n, idgen, g)
|
||||
of mAshrI:
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, idgen, g)
|
||||
of tyInt64, tyInt:
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, g)
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for ashr")
|
||||
of mDivI:
|
||||
let argA = getInt(a)
|
||||
let argB = getInt(b)
|
||||
if argB != Zero and (argA != firstOrd(g.config, n.typ) or argB != NegOne):
|
||||
result = newIntNodeT(argA div argB, n, g)
|
||||
result = newIntNodeT(argA div argB, n, idgen, g)
|
||||
of mModI:
|
||||
let argA = getInt(a)
|
||||
let argB = getInt(b)
|
||||
if argB != Zero and (argA != firstOrd(g.config, n.typ) or argB != NegOne):
|
||||
result = newIntNodeT(argA mod argB, n, g)
|
||||
result = newIntNodeT(argA mod argB, n, idgen, g)
|
||||
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)
|
||||
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g)
|
||||
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
|
||||
of mDivF64:
|
||||
result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
|
||||
of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, g)
|
||||
of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, idgen, g)
|
||||
of mLtI, mLtB, mLtEnum, mLtCh:
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, g)
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g)
|
||||
of mLeI, mLeB, mLeEnum, mLeCh:
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) <= getOrdValue(b))), n, g)
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) <= getOrdValue(b))), n, idgen, g)
|
||||
of mEqI, mEqB, mEqEnum, mEqCh:
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) == getOrdValue(b))), n, g)
|
||||
of mLtF64: result = newIntNodeT(toInt128(ord(getFloat(a) < getFloat(b))), n, g)
|
||||
of mLeF64: result = newIntNodeT(toInt128(ord(getFloat(a) <= getFloat(b))), n, g)
|
||||
of mEqF64: result = newIntNodeT(toInt128(ord(getFloat(a) == getFloat(b))), n, g)
|
||||
of mLtStr: result = newIntNodeT(toInt128(ord(getStr(a) < getStr(b))), n, g)
|
||||
of mLeStr: result = newIntNodeT(toInt128(ord(getStr(a) <= getStr(b))), n, g)
|
||||
of mEqStr: result = newIntNodeT(toInt128(ord(getStr(a) == getStr(b))), n, g)
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) == getOrdValue(b))), n, idgen, g)
|
||||
of mLtF64: result = newIntNodeT(toInt128(ord(getFloat(a) < getFloat(b))), n, idgen, g)
|
||||
of mLeF64: result = newIntNodeT(toInt128(ord(getFloat(a) <= getFloat(b))), n, idgen, g)
|
||||
of mEqF64: result = newIntNodeT(toInt128(ord(getFloat(a) == getFloat(b))), n, idgen, g)
|
||||
of mLtStr: result = newIntNodeT(toInt128(ord(getStr(a) < getStr(b))), n, idgen, g)
|
||||
of mLeStr: result = newIntNodeT(toInt128(ord(getStr(a) <= getStr(b))), n, idgen, g)
|
||||
of mEqStr: result = newIntNodeT(toInt128(ord(getStr(a) == getStr(b))), n, idgen, g)
|
||||
of mLtU:
|
||||
result = newIntNodeT(toInt128(ord(`<%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, g)
|
||||
result = newIntNodeT(toInt128(ord(`<%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, idgen, g)
|
||||
of mLeU:
|
||||
result = newIntNodeT(toInt128(ord(`<=%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, g)
|
||||
of mBitandI, mAnd: result = newIntNodeT(bitand(a.getInt, b.getInt), n, g)
|
||||
of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, g)
|
||||
of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, g)
|
||||
result = newIntNodeT(toInt128(ord(`<=%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, idgen, g)
|
||||
of mBitandI, mAnd: result = newIntNodeT(bitand(a.getInt, b.getInt), n, idgen, g)
|
||||
of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, idgen, g)
|
||||
of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, idgen, g)
|
||||
of mAddU:
|
||||
let val = maskBytes(getInt(a) + getInt(b), int(n.typ.size))
|
||||
result = newIntNodeT(val, n, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
of mSubU:
|
||||
let val = maskBytes(getInt(a) - getInt(b), int(n.typ.size))
|
||||
result = newIntNodeT(val, n, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
# echo "subU: ", val, " n: ", n, " result: ", val
|
||||
of mMulU:
|
||||
let val = maskBytes(getInt(a) * getInt(b), int(n.typ.size))
|
||||
result = newIntNodeT(val, n, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
of mModU:
|
||||
let argA = maskBytes(getInt(a), int(a.typ.size))
|
||||
let argB = maskBytes(getInt(b), int(a.typ.size))
|
||||
if argB != Zero:
|
||||
result = newIntNodeT(argA mod argB, n, g)
|
||||
result = newIntNodeT(argA mod argB, n, idgen, g)
|
||||
of mDivU:
|
||||
let argA = maskBytes(getInt(a), int(a.typ.size))
|
||||
let argB = maskBytes(getInt(b), int(a.typ.size))
|
||||
if argB != Zero:
|
||||
result = newIntNodeT(argA div argB, n, g)
|
||||
of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, g)
|
||||
of mEqSet: result = newIntNodeT(toInt128(ord(equalSets(g.config, a, b))), n, g)
|
||||
result = newIntNodeT(argA div argB, n, idgen, g)
|
||||
of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, idgen, g)
|
||||
of mEqSet: result = newIntNodeT(toInt128(ord(equalSets(g.config, a, b))), n, idgen, g)
|
||||
of mLtSet:
|
||||
result = newIntNodeT(toInt128(ord(
|
||||
containsSets(g.config, a, b) and not equalSets(g.config, a, b))), n, g)
|
||||
containsSets(g.config, a, b) and not equalSets(g.config, a, b))), n, idgen, g)
|
||||
of mMulSet:
|
||||
result = nimsets.intersectSets(g.config, a, b)
|
||||
result.info = n.info
|
||||
@@ -271,7 +282,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
result = nimsets.diffSets(g.config, a, b)
|
||||
result.info = n.info
|
||||
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
|
||||
of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, g)
|
||||
of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g)
|
||||
of mRepr:
|
||||
# BUGFIX: we cannot eval mRepr here for reasons that I forgot.
|
||||
discard
|
||||
@@ -294,13 +305,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
result = copyTree(a)
|
||||
result.typ = n.typ
|
||||
of mCompileOption:
|
||||
result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, g)
|
||||
result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g)
|
||||
of mCompileOptionArg:
|
||||
result = newIntNodeT(toInt128(ord(
|
||||
testCompileOptionArg(g.config, getStr(a), getStr(b), n.info))), n, g)
|
||||
testCompileOptionArg(g.config, getStr(a), getStr(b), n.info))), n, idgen, g)
|
||||
of mEqProc:
|
||||
result = newIntNodeT(toInt128(ord(
|
||||
exprStructuralEquivalent(a, b, strictSymEquality=true))), n, g)
|
||||
exprStructuralEquivalent(a, b, strictSymEquality=true))), n, idgen, g)
|
||||
else: discard
|
||||
|
||||
proc getConstIfExpr(c: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
@@ -346,7 +357,7 @@ proc magicCall(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
if n.len > 3:
|
||||
c = getConstExpr(m, n[3], idgen, g)
|
||||
if c == nil: return
|
||||
result = evalOp(s.magic, n, a, b, c, g)
|
||||
result = evalOp(s.magic, n, a, b, c, idgen, g)
|
||||
|
||||
proc getAppType(n: PNode; g: ModuleGraph): PNode =
|
||||
if g.config.globalOptions.contains(optGenDynLib):
|
||||
@@ -363,7 +374,7 @@ proc rangeCheck(n: PNode, value: Int128; g: ModuleGraph) =
|
||||
localError(g.config, n.info, "cannot convert " & $value &
|
||||
" to " & typeToString(n.typ))
|
||||
|
||||
proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
|
||||
proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): PNode =
|
||||
let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc})
|
||||
let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc})
|
||||
|
||||
@@ -377,9 +388,9 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
|
||||
of tyBool:
|
||||
case srcTyp.kind
|
||||
of tyFloat..tyFloat64:
|
||||
result = newIntNodeT(toInt128(getFloat(a) != 0.0), n, g)
|
||||
result = newIntNodeT(toInt128(getFloat(a) != 0.0), n, idgen, g)
|
||||
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
|
||||
result = newIntNodeT(toInt128(a.getOrdValue != 0), n, g)
|
||||
result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g)
|
||||
of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
@@ -387,11 +398,11 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
case srcTyp.kind
|
||||
of tyFloat..tyFloat64:
|
||||
result = newIntNodeT(toInt128(getFloat(a)), n, g)
|
||||
result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
|
||||
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
|
||||
var val = a.getOrdValue
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
if dstTyp.kind in {tyUInt..tyUInt64}:
|
||||
result.transitionIntKind(nkUIntLit)
|
||||
else:
|
||||
@@ -476,7 +487,7 @@ proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
|
||||
result = newSymNode(s, info)
|
||||
if s.typ.kind != tyTypeDesc:
|
||||
result.typ = newType(tyTypeDesc, idgen.nextId, s.owner)
|
||||
result.typ = newType(tyTypeDesc, idgen.nextTypeId, s.owner)
|
||||
result.typ.addSonSkipIntLit(s.typ, idgen)
|
||||
else:
|
||||
result.typ = s.typ
|
||||
@@ -488,13 +499,13 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
var s = n.sym
|
||||
case s.kind
|
||||
of skEnumField:
|
||||
result = newIntNodeT(toInt128(s.position), n, g)
|
||||
result = newIntNodeT(toInt128(s.position), n, idgen, g)
|
||||
of skConst:
|
||||
case s.magic
|
||||
of mIsMainModule: result = newIntNodeT(toInt128(ord(sfMainModule in m.flags)), n, g)
|
||||
of mIsMainModule: result = newIntNodeT(toInt128(ord(sfMainModule in m.flags)), n, idgen, g)
|
||||
of mCompileDate: result = newStrNodeT(getDateStr(), n, g)
|
||||
of mCompileTime: result = newStrNodeT(getClockStr(), n, g)
|
||||
of mCpuEndian: result = newIntNodeT(toInt128(ord(CPU[g.config.target.targetCPU].endian)), n, g)
|
||||
of mCpuEndian: result = newIntNodeT(toInt128(ord(CPU[g.config.target.targetCPU].endian)), n, idgen, g)
|
||||
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g)
|
||||
of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g)
|
||||
of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g)
|
||||
@@ -503,7 +514,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
of mIntDefine:
|
||||
if isDefined(g.config, s.name.s):
|
||||
try:
|
||||
result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseInt), n, g)
|
||||
result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseInt), n, idgen, g)
|
||||
except ValueError:
|
||||
localError(g.config, s.info,
|
||||
"{.intdefine.} const was set to an invalid integer: '" &
|
||||
@@ -518,7 +529,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
of mBoolDefine:
|
||||
if isDefined(g.config, s.name.s):
|
||||
try:
|
||||
result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseBool.int), n, g)
|
||||
result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseBool.int), n, idgen, g)
|
||||
except ValueError:
|
||||
localError(g.config, s.info,
|
||||
"{.booldefine.} const was set to an invalid bool: '" &
|
||||
@@ -564,30 +575,30 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64:
|
||||
result = newFloatNodeT(firstFloat(n[1].typ), n, g)
|
||||
else:
|
||||
result = newIntNodeT(firstOrd(g.config, n[1].typ), n, g)
|
||||
result = newIntNodeT(firstOrd(g.config, n[1].typ), n, idgen, g)
|
||||
of mHigh:
|
||||
if skipTypes(n[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin
|
||||
{tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
|
||||
if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64:
|
||||
result = newFloatNodeT(lastFloat(n[1].typ), n, g)
|
||||
else:
|
||||
result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g)
|
||||
result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, idgen, g)
|
||||
else:
|
||||
var a = getArrayConstr(m, n[1], idgen, g)
|
||||
if a.kind == nkBracket:
|
||||
# we can optimize it away:
|
||||
result = newIntNodeT(toInt128(a.len-1), n, g)
|
||||
result = newIntNodeT(toInt128(a.len-1), n, idgen, g)
|
||||
of mLengthOpenArray:
|
||||
var a = getArrayConstr(m, n[1], idgen, g)
|
||||
if a.kind == nkBracket:
|
||||
# we can optimize it away! This fixes the bug ``len(134)``.
|
||||
result = newIntNodeT(toInt128(a.len), n, g)
|
||||
result = newIntNodeT(toInt128(a.len), n, idgen, g)
|
||||
else:
|
||||
result = magicCall(m, n, idgen, g)
|
||||
of mLengthArray:
|
||||
# It doesn't matter if the argument is const or not for mLengthArray.
|
||||
# This fixes bug #544.
|
||||
result = newIntNodeT(lengthOrd(g.config, n[1].typ), n, g)
|
||||
result = newIntNodeT(lengthOrd(g.config, n[1].typ), n, idgen, g)
|
||||
of mSizeOf:
|
||||
result = foldSizeOf(g.config, n, nil)
|
||||
of mAlignOf:
|
||||
@@ -670,7 +681,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
var a = getConstExpr(m, n[1], idgen, g)
|
||||
if a == nil: return
|
||||
result = foldConv(n, a, g, check=true)
|
||||
result = foldConv(n, a, idgen, g, check=true)
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
let a = getConstExpr(m, n[0], idgen, g)
|
||||
if a != nil and a.kind == nkNilLit:
|
||||
|
||||
@@ -472,7 +472,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags, ctx)
|
||||
if n[paramsPos].kind != nkEmpty:
|
||||
if n[paramsPos][0].kind != nkEmpty:
|
||||
addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextId c.idgen, nil, n.info))
|
||||
addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info))
|
||||
n[paramsPos] = semGenericStmt(c, n[paramsPos], flags, ctx)
|
||||
n[pragmasPos] = semGenericStmt(c, n[pragmasPos], flags, ctx)
|
||||
var body: PNode
|
||||
@@ -481,7 +481,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
if sfGenSym in s.flags and s.ast == nil:
|
||||
body = n[bodyPos]
|
||||
else:
|
||||
body = s.getBody
|
||||
body = getBody(c.graph, s)
|
||||
else: body = n[bodyPos]
|
||||
n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
|
||||
closeScope(c)
|
||||
|
||||
@@ -62,30 +62,29 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
|
||||
for i, a in n.pairs:
|
||||
internalAssert c.config, a.kind == nkSym
|
||||
var q = a.sym
|
||||
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
|
||||
continue
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, nextId c.idgen, getCurrOwner(c), q.info)
|
||||
s.flags.incl {sfUsed, sfFromGeneric}
|
||||
var t = PType(idTableGet(pt, q.typ))
|
||||
if t == nil:
|
||||
if tfRetType in q.typ.flags:
|
||||
# keep the generic type and allow the return type to be bound
|
||||
# later by semAsgn in return type inference scenario
|
||||
t = q.typ
|
||||
else:
|
||||
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
|
||||
if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses:
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, nextSymId(c.idgen), getCurrOwner(c), q.info)
|
||||
s.flags.incl {sfUsed, sfFromGeneric}
|
||||
var t = PType(idTableGet(pt, q.typ))
|
||||
if t == nil:
|
||||
if tfRetType in q.typ.flags:
|
||||
# keep the generic type and allow the return type to be bound
|
||||
# later by semAsgn in return type inference scenario
|
||||
t = q.typ
|
||||
else:
|
||||
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind in {tyGenericParam, tyConcept}:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericParam:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericInvocation:
|
||||
#t = instGenericContainer(c, a, t)
|
||||
t = generateTypeInstance(c, pt, a, t)
|
||||
#t = ReplaceTypeVarsT(cl, t)
|
||||
s.typ = t
|
||||
if t.kind == tyStatic: s.ast = t.n
|
||||
yield s
|
||||
elif t.kind == tyGenericInvocation:
|
||||
#t = instGenericContainer(c, a, t)
|
||||
t = generateTypeInstance(c, pt, a, t)
|
||||
#t = ReplaceTypeVarsT(cl, t)
|
||||
s.typ = t
|
||||
if t.kind == tyStatic: s.ast = t.n
|
||||
yield s
|
||||
|
||||
proc sameInstantiation(a, b: TInstantiation): bool =
|
||||
if a.concreteTypes.len == b.concreteTypes.len:
|
||||
@@ -95,9 +94,9 @@ proc sameInstantiation(a, b: TInstantiation): bool =
|
||||
ExactGcSafety}): return
|
||||
result = true
|
||||
|
||||
proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
|
||||
proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
|
||||
id: CompilesId): PSym =
|
||||
for inst in genericSym.procInstCache:
|
||||
for inst in procInstCacheItems(g, genericSym):
|
||||
if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
|
||||
return inst.sym
|
||||
|
||||
@@ -118,7 +117,7 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable
|
||||
n.sym = x
|
||||
elif s.owner == nil or s.owner.kind == skPackage:
|
||||
#echo "copied this ", s.name.s
|
||||
x = copySym(s, nextId c.idgen)
|
||||
x = copySym(s, nextSymId c.idgen)
|
||||
x.owner = owner
|
||||
idTablePut(symMap, s, x)
|
||||
n.sym = x
|
||||
@@ -160,7 +159,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
||||
pushInfoContext(c.config, oldPrc.info)
|
||||
openScope(c)
|
||||
var n = oldPrc.ast
|
||||
n[bodyPos] = copyTree(s.getBody)
|
||||
n[bodyPos] = copyTree(getBody(c.graph, s))
|
||||
instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
|
||||
closeScope(c)
|
||||
popInfoContext(c.config)
|
||||
@@ -200,7 +199,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||
var param: PSym
|
||||
|
||||
template paramSym(kind): untyped =
|
||||
newSym(kind, genParam.sym.name, nextId c.idgen, genericTyp.sym, genParam.sym.info)
|
||||
newSym(kind, genParam.sym.name, nextSymId c.idgen, genericTyp.sym, genParam.sym.info)
|
||||
|
||||
if genParam.kind == tyStatic:
|
||||
param = paramSym skConst
|
||||
@@ -232,11 +231,11 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
# time adding the instantiated proc params into the current scope.
|
||||
# This is necessary, because the instantiation process may refer to
|
||||
# these params in situations like this:
|
||||
# proc foo[Container](a: Container, b: a.type.Item): type(b.x)
|
||||
# proc foo[Container](a: Container, b: a.type.Item): typeof(b.x)
|
||||
#
|
||||
# Alas, doing this here is probably not enough, because another
|
||||
# proc signature could appear in the params:
|
||||
# proc foo[T](a: proc (x: T, b: type(x.y))
|
||||
# proc foo[T](a: proc (x: T, b: typeof(x.y))
|
||||
#
|
||||
# The solution would be to move this logic into semtypinst, but
|
||||
# at this point semtypinst have to become part of sem, because it
|
||||
@@ -270,7 +269,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam, nextId c.idgen)
|
||||
let param = copySym(oldParam, nextSymId c.idgen)
|
||||
param.owner = prc
|
||||
param.typ = result[i]
|
||||
|
||||
@@ -319,6 +318,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
prc.typ = result
|
||||
popInfoContext(c.config)
|
||||
|
||||
proc fillMixinScope(c: PContext) =
|
||||
var p = c.p
|
||||
while p != nil:
|
||||
for bnd in p.localBindStmts:
|
||||
for n in bnd:
|
||||
addSym(c.currentScope, n.sym)
|
||||
p = p.next
|
||||
|
||||
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
info: TLineInfo): PSym {.nosinks.} =
|
||||
## Generates a new instance of a generic procedure.
|
||||
@@ -339,15 +346,19 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
c.matchedConcept = nil
|
||||
let oldScope = c.currentScope
|
||||
while not isTopLevel(c): c.currentScope = c.currentScope.parent
|
||||
result = copySym(fn, nextId c.idgen)
|
||||
result = copySym(fn, nextSymId c.idgen)
|
||||
incl(result.flags, sfFromGeneric)
|
||||
result.owner = fn
|
||||
result.ast = n
|
||||
pushOwner(c, result)
|
||||
|
||||
# mixin scope:
|
||||
openScope(c)
|
||||
fillMixinScope(c)
|
||||
|
||||
openScope(c)
|
||||
let gp = n[genericParamsPos]
|
||||
internalAssert c.config, gp.kind != nkEmpty
|
||||
internalAssert c.config, gp.kind == nkGenericParams
|
||||
n[namePos] = newSymNode(result)
|
||||
pushInfoContext(c.config, info, fn.detailedInfo)
|
||||
var entry = TInstantiation.new
|
||||
@@ -369,7 +380,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
if tfTriggersCompileTime in result.typ.flags:
|
||||
incl(result.flags, sfCompileTime)
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
|
||||
var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
|
||||
if oldPrc == nil:
|
||||
# we MUST not add potentially wrong instantiations to the caching mechanism.
|
||||
# This means recursive instantiations behave differently when in
|
||||
@@ -378,12 +389,12 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
#if c.compilesContextId == 0:
|
||||
rawHandleSelf(c, result)
|
||||
entry.compilesId = c.compilesContextId
|
||||
fn.procInstCache.add(entry)
|
||||
addToGenericProcCache(c, fn, entry)
|
||||
c.generics.add(makeInstPair(fn, entry))
|
||||
if n[pragmasPos].kind != nkEmpty:
|
||||
pragma(c, result, n[pragmasPos], allRoutinePragmas)
|
||||
if isNil(n[bodyPos]):
|
||||
n[bodyPos] = copyTree(fn.getBody)
|
||||
n[bodyPos] = copyTree(getBody(c.graph, fn))
|
||||
if c.inGenericContext == 0:
|
||||
instantiateBody(c, n, fn.typ.n, result, fn)
|
||||
sideEffectsCheck(c, result)
|
||||
@@ -395,9 +406,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
popProcCon(c)
|
||||
popInfoContext(c.config)
|
||||
closeScope(c) # close scope for parameters
|
||||
closeScope(c) # close scope for 'mixin' declarations
|
||||
popOwner(c)
|
||||
c.currentScope = oldScope
|
||||
discard c.friendModules.pop()
|
||||
dec(c.instCounter)
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
if result.kind == skMethod: finishMethod(c, result)
|
||||
|
||||
# inform IC of the generic
|
||||
#addGeneric(c.ic, result, entry.concreteTypes)
|
||||
|
||||
@@ -71,7 +71,7 @@ proc semAsgnOpr(c: PContext; n: PNode): PNode =
|
||||
|
||||
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
var r = isPartOf(n[1], n[2])
|
||||
result = newIntNodeT(toInt128(ord(r)), n, c.graph)
|
||||
result = newIntNodeT(toInt128(ord(r)), n, c.idgen, c.graph)
|
||||
|
||||
proc expectIntLit(c: PContext, n: PNode): int =
|
||||
let x = c.semConstExpr(c, n)
|
||||
@@ -119,7 +119,7 @@ proc uninstantiate(t: PType): PType =
|
||||
else: t
|
||||
|
||||
proc getTypeDescNode(c: PContext; typ: PType, sym: PSym, info: TLineInfo): PNode =
|
||||
var resType = newType(tyTypeDesc, nextId c.idgen, sym)
|
||||
var resType = newType(tyTypeDesc, nextTypeId c.idgen, sym)
|
||||
rawAddSon(resType, typ)
|
||||
result = toNode(resType, info)
|
||||
|
||||
@@ -160,7 +160,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
result.info = traitCall.info
|
||||
of "arity":
|
||||
result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
|
||||
result.typ = newType(tyInt, nextId c.idgen, context)
|
||||
result.typ = newType(tyInt, nextTypeId c.idgen, context)
|
||||
result.info = traitCall.info
|
||||
of "genericHead":
|
||||
var arg = operand
|
||||
@@ -172,33 +172,28 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
# result = toNode(resType, traitCall.info) # doesn't work yet
|
||||
else:
|
||||
localError(c.config, traitCall.info, "expected generic type, got: type $2 of kind $1" % [arg.kind.toHumanStr, typeToString(operand)])
|
||||
result = newType(tyError, nextId c.idgen, context).toNode(traitCall.info)
|
||||
result = newType(tyError, nextTypeId c.idgen, context).toNode(traitCall.info)
|
||||
of "stripGenericParams":
|
||||
result = uninstantiate(operand).toNode(traitCall.info)
|
||||
of "supportsCopyMem":
|
||||
let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
|
||||
let complexObj = containsGarbageCollectedRef(t) or
|
||||
hasDestructor(t)
|
||||
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.graph)
|
||||
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
|
||||
of "isNamedTuple":
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
let cond = operand.kind == tyTuple and operand.n != nil
|
||||
result = newIntNodeT(toInt128(ord(cond)), traitCall, c.graph)
|
||||
result = newIntNodeT(toInt128(ord(cond)), traitCall, c.idgen, c.graph)
|
||||
of "tupleLen":
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
assert operand.kind == tyTuple, $operand.kind
|
||||
result = newIntNodeT(toInt128(operand.len), traitCall, c.graph)
|
||||
result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
|
||||
of "distinctBase":
|
||||
var arg = operand.skipTypes({tyGenericInst})
|
||||
if arg.kind == tyDistinct:
|
||||
while arg.kind == tyDistinct:
|
||||
arg = arg.base
|
||||
arg = arg.skipTypes(skippedTypes + {tyGenericInst})
|
||||
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
|
||||
else:
|
||||
localError(c.config, traitCall.info,
|
||||
"distinctBase expects a distinct type as argument. The given type was " & typeToString(operand))
|
||||
result = newType(tyError, nextId c.idgen, context).toNode(traitCall.info)
|
||||
while arg.kind == tyDistinct:
|
||||
arg = arg.base
|
||||
arg = arg.skipTypes(skippedTypes + {tyGenericInst})
|
||||
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
|
||||
else:
|
||||
localError(c.config, traitCall.info, "unknown trait: " & s)
|
||||
result = newNodeI(nkEmpty, traitCall.info)
|
||||
@@ -370,7 +365,7 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
elems[i] = unownedType(c, t[i])
|
||||
if elems[i] != t[i]: someChange = true
|
||||
if someChange:
|
||||
result = newType(tyTuple, nextId c.idgen, t.owner)
|
||||
result = newType(tyTuple, nextTypeId c.idgen, t.owner)
|
||||
# we have to use 'rawAddSon' here so that type flags are
|
||||
# properly computed:
|
||||
for e in elems: result.rawAddSon(e)
|
||||
@@ -381,7 +376,9 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
tyGenericInst, tyAlias:
|
||||
let b = unownedType(c, t[^1])
|
||||
if b != t[^1]:
|
||||
result = copyType(t, nextId c.idgen, t.owner)
|
||||
result = copyType(t, nextTypeId c.idgen, t.owner)
|
||||
copyTypeProps(c.graph, c.idgen.module, result, t)
|
||||
|
||||
result[^1] = b
|
||||
result.flags.excl tfHasOwned
|
||||
else:
|
||||
@@ -407,26 +404,26 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
if n.sym == oldParam:
|
||||
result.sym = newParam
|
||||
elif n.sym.owner == orig:
|
||||
result.sym = copySym(n.sym, nextId c.idgen)
|
||||
result.sym = copySym(n.sym, nextSymId c.idgen)
|
||||
result.sym.owner = procSym
|
||||
for i in 0 ..< safeLen(n):
|
||||
result[i] = transform(c, procSym, n[i], old, fresh, oldParam, newParam)
|
||||
#if n.kind == nkDerefExpr and sameType(n[0].typ, old):
|
||||
# result =
|
||||
|
||||
result = copySym(orig, nextId c.idgen)
|
||||
result = copySym(orig, nextSymId c.idgen)
|
||||
result.info = info
|
||||
result.flags.incl sfFromGeneric
|
||||
result.owner = orig
|
||||
let origParamType = orig.typ[1]
|
||||
let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
|
||||
let oldParam = orig.typ.n[1].sym
|
||||
let newParam = newSym(skParam, oldParam.name, nextId c.idgen, result, result.info)
|
||||
let newParam = newSym(skParam, oldParam.name, nextSymId c.idgen, result, result.info)
|
||||
newParam.typ = newParamType
|
||||
# proc body:
|
||||
result.ast = transform(c, result, orig.ast, origParamType, newParamType, oldParam, newParam)
|
||||
# proc signature:
|
||||
result.typ = newProcType(result.info, nextId c.idgen, result)
|
||||
result.typ = newProcType(result.info, nextTypeId c.idgen, result)
|
||||
result.typ.addParam newParam
|
||||
|
||||
proc semQuantifier(c: PContext; n: PNode): PNode =
|
||||
@@ -528,10 +525,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
|
||||
elif optTinyRtti in c.config.globalOptions:
|
||||
let nfin = skipConvCastAndClosure(n[^1])
|
||||
let fin = case nfin.kind
|
||||
let fin = case nfin.kind
|
||||
of nkSym: nfin.sym
|
||||
of nkLambda, nkDo: nfin[namePos].sym
|
||||
else:
|
||||
else:
|
||||
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
|
||||
nil
|
||||
if fin != nil:
|
||||
@@ -541,7 +538,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
|
||||
# check if we converted this finalizer into a destructor already:
|
||||
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
|
||||
if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == fin:
|
||||
if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
|
||||
getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
|
||||
discard "already turned this one into a finalizer"
|
||||
else:
|
||||
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
|
||||
@@ -549,8 +547,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mDestroy:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
if t.destructor != nil:
|
||||
result[0] = newSymNode(t.destructor)
|
||||
let op = getAttachedOp(c.graph, t, attachedDestructor)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
of mUnown:
|
||||
result = semUnown(c, n)
|
||||
of mExists, mForall:
|
||||
|
||||
@@ -153,6 +153,7 @@ proc collectMissingFields(c: PContext, fieldsRecList: PNode,
|
||||
if assignment == nil:
|
||||
constrCtx.missingFields.add r.sym
|
||||
|
||||
|
||||
proc semConstructFields(c: PContext, n: PNode,
|
||||
constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): InitStatus =
|
||||
|
||||
@@ -81,7 +81,7 @@ proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx =
|
||||
result.slices = @[]
|
||||
result.args = @[]
|
||||
result.guards.s = @[]
|
||||
result.guards.o = initOperators(g)
|
||||
result.guards.g = g
|
||||
result.graph = g
|
||||
|
||||
proc lookupSlot(c: AnalysisCtx; s: PSym): int =
|
||||
@@ -138,7 +138,7 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) =
|
||||
|
||||
proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
|
||||
checkLe(c, lowBound(c.graph.config, arr), idx)
|
||||
checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o))
|
||||
checkLe(c, idx, highBound(c.graph.config, arr, c.graph.operators))
|
||||
|
||||
proc addLowerBoundAsFacts(c: var AnalysisCtx) =
|
||||
for v in c.locals:
|
||||
@@ -147,8 +147,8 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) =
|
||||
|
||||
proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
|
||||
checkLocal(c, n)
|
||||
let le = le.canon(c.guards.o)
|
||||
let ri = ri.canon(c.guards.o)
|
||||
let le = le.canon(c.graph.operators)
|
||||
let ri = ri.canon(c.graph.operators)
|
||||
# perform static bounds checking here; and not later!
|
||||
let oldState = c.guards.s.len
|
||||
addLowerBoundAsFacts(c)
|
||||
@@ -192,7 +192,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode =
|
||||
if isLocal(n):
|
||||
let s = c.lookupSlot(n.sym)
|
||||
if s >= 0 and c.locals[s].stride != nil:
|
||||
result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o)
|
||||
result = buildAdd(n, c.locals[s].stride.intVal, c.graph.operators)
|
||||
else:
|
||||
result = n
|
||||
elif n.safeLen > 0:
|
||||
@@ -307,16 +307,16 @@ proc analyseCase(c: var AnalysisCtx; n: PNode) =
|
||||
proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
||||
analyse(c, n[0][0])
|
||||
let oldFacts = c.guards.s.len
|
||||
addFact(c.guards, canon(n[0][0], c.guards.o))
|
||||
addFact(c.guards, canon(n[0][0], c.graph.operators))
|
||||
|
||||
analyse(c, n[0][1])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
setLen(c.guards.s, oldFacts)
|
||||
for j in 0..i-1:
|
||||
addFactNeg(c.guards, canon(n[j][0], c.guards.o))
|
||||
addFactNeg(c.guards, canon(n[j][0], c.graph.operators))
|
||||
if branch.len > 1:
|
||||
addFact(c.guards, canon(branch[0], c.guards.o))
|
||||
addFact(c.guards, canon(branch[0], c.graph.operators))
|
||||
for i in 0..<branch.len:
|
||||
analyse(c, branch[i])
|
||||
setLen(c.guards.s, oldFacts)
|
||||
@@ -382,26 +382,27 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||
# loop may never execute:
|
||||
let oldState = c.locals.len
|
||||
let oldFacts = c.guards.s.len
|
||||
addFact(c.guards, canon(n[0], c.guards.o))
|
||||
addFact(c.guards, canon(n[0], c.graph.operators))
|
||||
analyse(c, n[1])
|
||||
setLen(c.locals, oldState)
|
||||
setLen(c.guards.s, oldFacts)
|
||||
# we know after the loop the negation holds:
|
||||
if not hasSubnodeWith(n[1], nkBreakStmt):
|
||||
addFactNeg(c.guards, canon(n[0], c.guards.o))
|
||||
addFactNeg(c.guards, canon(n[0], c.graph.operators))
|
||||
dec c.inLoop
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
|
||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef,
|
||||
nkMixinStmt, nkBindStmt, nkExportStmt:
|
||||
discard
|
||||
else:
|
||||
analyseSons(c, n)
|
||||
|
||||
proc transformSlices(g: ModuleGraph; n: PNode): PNode =
|
||||
proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode =
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
let op = n[0].sym
|
||||
if op.name.s == "[]" and op.fromSystem:
|
||||
result = copyNode(n)
|
||||
let opSlice = newSymNode(createMagic(g, "slice", mSlice))
|
||||
let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice))
|
||||
opSlice.typ = getSysType(g, n.info, tyInt)
|
||||
result.add opSlice
|
||||
result.add n[1]
|
||||
@@ -412,11 +413,11 @@ proc transformSlices(g: ModuleGraph; n: PNode): PNode =
|
||||
if n.safeLen > 0:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transformSlices(g, n[i])
|
||||
result[i] = transformSlices(g, idgen, n[i])
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc transformSpawn(g: ModuleGraph; idgen: IdGenerator;owner: PSym; n, barrier: PNode): PNode
|
||||
proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode
|
||||
proc transformSpawnSons(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode =
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
@@ -430,7 +431,7 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier:
|
||||
let b = it.lastSon
|
||||
if getMagic(b) == mSpawn:
|
||||
if it.len != 3: localError(g.config, it.info, "invalid context for 'spawn'")
|
||||
let m = transformSlices(g, b)
|
||||
let m = transformSlices(g, idgen, b)
|
||||
if result.isNil:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
result.add n
|
||||
@@ -445,12 +446,12 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier:
|
||||
let b = n[1]
|
||||
if getMagic(b) == mSpawn and (let t = b[1][0].typ[0];
|
||||
spawnResult(t, true) == srByVar):
|
||||
let m = transformSlices(g, b)
|
||||
let m = transformSlices(g, idgen, b)
|
||||
return wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, n[0])
|
||||
result = transformSpawnSons(g, idgen, owner, n, barrier)
|
||||
of nkCallKinds:
|
||||
if getMagic(n) == mSpawn:
|
||||
result = transformSlices(g, n)
|
||||
result = transformSlices(g, idgen, n)
|
||||
return wrapProcForSpawn(g, idgen, owner, result, n.typ, barrier, nil)
|
||||
result = transformSpawnSons(g, idgen, owner, n, barrier)
|
||||
elif n.safeLen > 0:
|
||||
@@ -482,7 +483,7 @@ proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): P
|
||||
checkArgs(a, body)
|
||||
|
||||
var varSection = newNodeI(nkVarSection, n.info)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "barrier"), nextId idgen, owner, n.info)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "barrier"), nextSymId idgen, owner, n.info)
|
||||
temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
let tempNode = newSymNode(temp)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import
|
||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
|
||||
modulegraphs, varpartitions, typeallowed
|
||||
modulegraphs, varpartitions, typeallowed, nilcheck
|
||||
|
||||
when defined(useDfa):
|
||||
import dfa
|
||||
@@ -70,7 +70,7 @@ type
|
||||
guards: TModel # nested guards
|
||||
locked: seq[PNode] # locked locations
|
||||
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
hasDangerousAssign: bool
|
||||
hasDangerousAssign, isInnerProc: bool
|
||||
inEnforcedNoSideEffects: bool
|
||||
maxLockLevel, currLockLevel: TLockLevel
|
||||
currOptions: TOptions
|
||||
@@ -219,7 +219,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
|
||||
if reason.kind == nkSym:
|
||||
a.owner.gcUnsafetyReason = reason.sym
|
||||
else:
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextId a.c.idgen,
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextSymId a.c.idgen,
|
||||
a.owner, reason.info, {})
|
||||
|
||||
when true:
|
||||
@@ -269,6 +269,9 @@ proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) =
|
||||
markSideEffect(a, s)
|
||||
else:
|
||||
markSideEffect(a, s)
|
||||
if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and
|
||||
{sfGlobal, sfThread} * s.flags == {}:
|
||||
a.isInnerProc = true
|
||||
|
||||
proc useVar(a: PEffects, n: PNode) =
|
||||
let s = n.sym
|
||||
@@ -723,7 +726,7 @@ proc checkLe(c: PEffects; a, b: PNode) =
|
||||
|
||||
proc checkBounds(c: PEffects; arr, idx: PNode) =
|
||||
checkLe(c, lowBound(c.config, arr), idx)
|
||||
checkLe(c, idx, highBound(c.config, arr, c.guards.o))
|
||||
checkLe(c, idx, highBound(c.config, arr, c.guards.g.operators))
|
||||
|
||||
proc checkRange(c: PEffects; value: PNode; typ: PType) =
|
||||
let t = typ.skipTypes(abstractInst - {tyRange})
|
||||
@@ -818,9 +821,9 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
if opKind != -1:
|
||||
# rebind type bounds operations after createTypeBoundOps call
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]:
|
||||
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
|
||||
createTypeBoundOps(tracked, t, n.info)
|
||||
let op = t.attachedOps[TTypeAttachedOp(opKind)]
|
||||
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
|
||||
if op != nil:
|
||||
n[0].sym = op
|
||||
|
||||
@@ -1236,7 +1239,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC
|
||||
t.ownerModule = s.getModule
|
||||
t.init = @[]
|
||||
t.guards.s = @[]
|
||||
t.guards.o = initOperators(g)
|
||||
t.guards.g = g
|
||||
when defined(drnim):
|
||||
t.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
|
||||
else:
|
||||
@@ -1252,6 +1255,15 @@ proc hasRealBody(s: PSym): bool =
|
||||
## which is not a real implementation, refs #14314
|
||||
result = {sfForward, sfImportc} * s.flags == {}
|
||||
|
||||
proc maybeWrappedInClosure(tracked: PEffects; t: PType): bool {.inline.} =
|
||||
## The spec does say when to produce destructors. However, the spec
|
||||
## was written in mind with the idea that "lambda lifting" already
|
||||
## happened. Not true in our implementation, so we need to workaround
|
||||
## here:
|
||||
result = tracked.isInnerProc and
|
||||
sfSystemModule notin tracked.c.module.flags and
|
||||
tfCheckedForDestructor notin t.flags and containsGarbageCollectedRef(t)
|
||||
|
||||
proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
let g = c.graph
|
||||
var effects = s.typ.n[0]
|
||||
@@ -1270,7 +1282,8 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
let param = params[i].sym
|
||||
let typ = param.typ
|
||||
if isSinkTypeForParam(typ) or
|
||||
(t.config.selectedGC in {gcArc, gcOrc} and isClosure(typ.skipTypes(abstractInst))):
|
||||
(t.config.selectedGC in {gcArc, gcOrc} and
|
||||
(isClosure(typ.skipTypes(abstractInst)) or maybeWrappedInClosure(t, typ))):
|
||||
createTypeBoundOps(t, typ, param.info)
|
||||
when false:
|
||||
if typ.kind == tyOut and param.id notin t.init:
|
||||
@@ -1310,7 +1323,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
var goals: set[Goal] = {}
|
||||
if strictFuncs in c.features: goals.incl constParameters
|
||||
if views in c.features: goals.incl borrowChecking
|
||||
var partitions = computeGraphPartitions(s, body, g.config, goals)
|
||||
var partitions = computeGraphPartitions(s, body, g, goals)
|
||||
if not t.hasSideEffect and t.hasDangerousAssign:
|
||||
t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo)
|
||||
if views in c.features:
|
||||
@@ -1344,7 +1357,10 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
when defined(useDfa):
|
||||
if s.name.s == "testp":
|
||||
dataflowAnalysis(s, body)
|
||||
|
||||
when false: trackWrites(s, body)
|
||||
if strictNotNil in c.features and s.kind == skProc:
|
||||
checkNil(s, body, g.config, c.idgen)
|
||||
|
||||
proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user