This commit is contained in:
flywind
2021-03-22 10:46:47 +08:00
830 changed files with 36431 additions and 48845 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,9 +29,8 @@
## "A GraphFree Approach to DataFlow 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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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