mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
203 Commits
pr_shadowi
...
v2.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c34f62785 | ||
|
|
46e1322d29 | ||
|
|
27b54fdc76 | ||
|
|
d594e70d57 | ||
|
|
6bf9265d24 | ||
|
|
a627c9ba9c | ||
|
|
7cec03eb1b | ||
|
|
123a7ff29f | ||
|
|
c0d50ddc26 | ||
|
|
2193c3fb70 | ||
|
|
c2b825713c | ||
|
|
ae011eaeea | ||
|
|
8fe518ed47 | ||
|
|
64927c6ae7 | ||
|
|
21c0564573 | ||
|
|
12347eae74 | ||
|
|
52cadfc3d7 | ||
|
|
528e2b2271 | ||
|
|
0c0df28619 | ||
|
|
a2a1c2b7f1 | ||
|
|
41c4ed8dca | ||
|
|
f6167cb0c8 | ||
|
|
72ce16990b | ||
|
|
ea3a4203fa | ||
|
|
24b24c17b1 | ||
|
|
e80884665d | ||
|
|
f4009f0957 | ||
|
|
728a99d5cd | ||
|
|
019a3180f8 | ||
|
|
fb13e3608b | ||
|
|
f5804a36b9 | ||
|
|
a83c535ed4 | ||
|
|
3c71429eca | ||
|
|
9e1b199e78 | ||
|
|
b2f2b34fe5 | ||
|
|
7be1bb572e | ||
|
|
40476fa24f | ||
|
|
3054cbe422 | ||
|
|
b31aed01f1 | ||
|
|
9c7f04dacb | ||
|
|
c5ee216c42 | ||
|
|
ede6540c55 | ||
|
|
0c14372a8c | ||
|
|
6deb3a90ae | ||
|
|
e0e1061562 | ||
|
|
d96c5b2396 | ||
|
|
09835a1d9e | ||
|
|
0823b9d177 | ||
|
|
b5068f427a | ||
|
|
1cee3c7f18 | ||
|
|
844ba2168b | ||
|
|
d5437b7a4a | ||
|
|
faa9ae08b0 | ||
|
|
7b596b4e1f | ||
|
|
cd06f0769f | ||
|
|
0cce145dac | ||
|
|
43f7e160ba | ||
|
|
5a71c36d25 | ||
|
|
1299dd4651 | ||
|
|
51a0f3de6e | ||
|
|
e2b6021630 | ||
|
|
428b64251f | ||
|
|
5b0b90fb49 | ||
|
|
2f5481ce88 | ||
|
|
9aeb5c254c | ||
|
|
4e1bc4216a | ||
|
|
316141162b | ||
|
|
8d8a90e079 | ||
|
|
105e134c3f | ||
|
|
85c8b5b304 | ||
|
|
2d470c9afd | ||
|
|
94a6b85538 | ||
|
|
90c5dfc32c | ||
|
|
a7b671dad5 | ||
|
|
fb11c4404e | ||
|
|
1adcab885b | ||
|
|
5ddbf2372e | ||
|
|
8f668c2373 | ||
|
|
3daf7dd2ac | ||
|
|
4b4b97018b | ||
|
|
aa5fc4af58 | ||
|
|
062e77bce0 | ||
|
|
d04f9c426e | ||
|
|
52809cd3dd | ||
|
|
05a8b65eea | ||
|
|
b1a555dd52 | ||
|
|
f5453e453e | ||
|
|
7d425e712e | ||
|
|
e6f5e49184 | ||
|
|
cd370e4725 | ||
|
|
42184227aa | ||
|
|
14ce1a91ce | ||
|
|
ff7b83f266 | ||
|
|
522b184d5a | ||
|
|
d339c58628 | ||
|
|
01b6c5d0d1 | ||
|
|
1bc501f8ce | ||
|
|
aa8d62f89c | ||
|
|
60a8eaaaa5 | ||
|
|
f3da96d880 | ||
|
|
87c306061b | ||
|
|
2b1885a0fa | ||
|
|
9c87f2cb4b | ||
|
|
03b6999499 | ||
|
|
2690ab01c0 | ||
|
|
c79fb859f1 | ||
|
|
2d658e8de5 | ||
|
|
0036bb976b | ||
|
|
b0f3d1e874 | ||
|
|
9f03b98de5 | ||
|
|
0b7e22635e | ||
|
|
3642f4d375 | ||
|
|
f292393816 | ||
|
|
6ec663f7bc | ||
|
|
6d02ac1ba0 | ||
|
|
ce1fa86095 | ||
|
|
4900550e9c | ||
|
|
9fe2356e74 | ||
|
|
37ab27bd99 | ||
|
|
5e22d8bc3c | ||
|
|
6b102d5c13 | ||
|
|
51f8649e36 | ||
|
|
df27b427af | ||
|
|
e57f755b78 | ||
|
|
d78c7aa697 | ||
|
|
435a152c66 | ||
|
|
6c2de9b294 | ||
|
|
babc7d8c16 | ||
|
|
022bd12e82 | ||
|
|
3c528c987c | ||
|
|
52d94c1c86 | ||
|
|
87de7f9193 | ||
|
|
520b16b81e | ||
|
|
98403a06e7 | ||
|
|
4bdeddcac5 | ||
|
|
9be3559ed2 | ||
|
|
f2a9765014 | ||
|
|
ac8c44e08d | ||
|
|
850132d37c | ||
|
|
a1ee2ee566 | ||
|
|
9306b5e917 | ||
|
|
67a636bec8 | ||
|
|
9a6230ee5a | ||
|
|
7e840e0164 | ||
|
|
cd760b00c2 | ||
|
|
0cce80071b | ||
|
|
5aeabdac8f | ||
|
|
2d678fa45c | ||
|
|
ee1c4de48a | ||
|
|
b3e02ef0c3 | ||
|
|
893c638485 | ||
|
|
9f51b52f5f | ||
|
|
bcfb30a8be | ||
|
|
7948e2f2c2 | ||
|
|
6b31400ade | ||
|
|
a713aee682 | ||
|
|
aa7e7f5f63 | ||
|
|
8d7b3baf9f | ||
|
|
274cdba334 | ||
|
|
a04dada93d | ||
|
|
4d170ac586 | ||
|
|
e3a8d98626 | ||
|
|
41145210a8 | ||
|
|
3c2b32aebe | ||
|
|
613f1e94ae | ||
|
|
660a9cecf0 | ||
|
|
bffd2e0330 | ||
|
|
d357a2e9a5 | ||
|
|
fca3504105 | ||
|
|
bf45efb1ea | ||
|
|
517a2fc275 | ||
|
|
4c56f9d675 | ||
|
|
de93f82d6e | ||
|
|
fa1819eb2d | ||
|
|
0fde5a0cc2 | ||
|
|
090139eb6f | ||
|
|
ee4bf757ea | ||
|
|
b0b4b498c8 | ||
|
|
d102571d78 | ||
|
|
1f418de2cc | ||
|
|
21bdc8ff0f | ||
|
|
9f7b664836 | ||
|
|
b24f58183d | ||
|
|
b8efee444c | ||
|
|
336549c49d | ||
|
|
2d4e1f981e | ||
|
|
13110fc5d3 | ||
|
|
700ca2eb60 | ||
|
|
5945ad41a1 | ||
|
|
7f113dc875 | ||
|
|
e13f86a596 | ||
|
|
6c96892d5e | ||
|
|
dd0cc389bb | ||
|
|
75e50f804a | ||
|
|
599f1ad6b3 | ||
|
|
d991600a00 | ||
|
|
b873eaedf5 | ||
|
|
39a6106e8b | ||
|
|
e262d9506d | ||
|
|
ddc7f35e05 | ||
|
|
f70a17f885 | ||
|
|
7a79f465fa | ||
|
|
b6450a98ea |
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
|
||||
76
.github/workflows/ci_gcc14.yml
vendored
76
.github/workflows/ci_gcc14.yml
vendored
@@ -1,76 +0,0 @@
|
||||
name: GCC 14
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt remove needrestart
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (Linux amd64 gcc 14)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update -qq
|
||||
sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'NIM_TESTAMENT_DISABLE_SSL'
|
||||
shell: bash
|
||||
run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
|
||||
|
||||
- name: 'System information'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimCiSystemInfo
|
||||
|
||||
- name: 'Build csourcesAny'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'koch, Run CI'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
3
.github/workflows/ci_packages.yml
vendored
3
.github/workflows/ci_packages.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
- 'version-2-2'
|
||||
- 'version-2-0'
|
||||
- 'version-1-6'
|
||||
- 'version-1-2'
|
||||
@@ -17,7 +18,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-13]
|
||||
os: [ubuntu-22.04, macos-13]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
|
||||
17
.github/workflows/ci_publish.yml
vendored
17
.github/workflows/ci_publish.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
os: [ubuntu-22.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
- name: 'Install node.js'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version: ''
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -34,17 +34,6 @@ jobs:
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
nimInternalInstallDepsWindows
|
||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
vmImage: 'ubuntu-24.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
# Linux_i386:
|
||||
@@ -80,10 +80,12 @@ jobs:
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo apt-fast update -qq
|
||||
echo_run sudo add-apt-repository universe
|
||||
echo_run sudo apt-get update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo apt-get install --no-install-recommends -yq \
|
||||
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
displayName: 'Install dependencies (amd64 Linux)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
||||
|
||||
@@ -100,15 +102,16 @@ jobs:
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# echo_run sudo apt-fast update -qq
|
||||
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
|
||||
# echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
|
||||
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
|
||||
# `could not load: libffi.so` during dynamic loading.
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
||||
echo_run sudo apt-get install --no-install-recommends --allow-downgrades -yq \
|
||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
||||
|
||||
|
||||
cat << EOF > bin/gcc
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
41
changelog.md
41
changelog.md
@@ -12,6 +12,13 @@ rounding guarantees (via the
|
||||
avoid conflicts with `system.default`, so named argument usage for this
|
||||
parameter like `getOrDefault(..., default = ...)` will have to be changed.
|
||||
|
||||
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
|
||||
|
||||
- Unknown warnings and hints now gives warnings `warnUnknownNotes` instead of
|
||||
errors.
|
||||
|
||||
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Additions:"
|
||||
@@ -24,6 +31,40 @@ rounding guarantees (via the
|
||||
|
||||
## Language changes
|
||||
|
||||
- An experimental option `--experimental:typeBoundOps` has been added that
|
||||
implements the RFC https://github.com/nim-lang/RFCs/issues/380.
|
||||
This makes the behavior of interfaces like `hash`, `$`, `==` etc. more
|
||||
reliable for nominal types across indirect/restricted imports.
|
||||
|
||||
```nim
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
|
||||
proc hash*(a: Obj): Hash =
|
||||
$!(hash(a.x) &! hash(a.y))
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
from objs import Obj # objs.hash, objs.`==` not imported
|
||||
import std/tables
|
||||
|
||||
var t: Table[Obj, int]
|
||||
t[Obj(x: 3, y: 4, z: "debug")] = 34
|
||||
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
|
||||
```
|
||||
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#typeminusbound-overloads)
|
||||
for more information.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
|
||||
|
||||
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
|
||||
now produces a `CountTable` with values correspoding to the number of occurrences
|
||||
now produces a `CountTable` with values corresponding to the number of occurrences
|
||||
of the key in the input. It used to produce a table with all values set to `1`.
|
||||
|
||||
Counting occurrences in a sequence used to be:
|
||||
@@ -339,7 +339,7 @@ for i in a..b:
|
||||
|
||||
- Fixed "ReraiseError when using try/except within finally block"
|
||||
([#5871](https://github.com/nim-lang/Nim/issues/5871))
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "JSON % operator can fail in extern procs with dynamic types"
|
||||
([#6385](https://github.com/nim-lang/Nim/issues/6385))
|
||||
@@ -403,7 +403,7 @@ for i in a..b:
|
||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
||||
- Fixed "Generated c code calls function twice"
|
||||
([#6292](https://github.com/nim-lang/Nim/issues/6292))
|
||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
||||
- Fixed "Range type inference leads to counter-intuitive behaviour"
|
||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
||||
- Fixed "New backward indexing is too limited"
|
||||
([#6631](https://github.com/nim-lang/Nim/issues/6631))
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- We removed `unicode.Rune16` without any deprecation period as the name
|
||||
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
|
||||
and we didn't find any usages of it in the wild. If you still need it, add this
|
||||
and we didn't find any usage of it in the wild. If you still need it, add this
|
||||
piece of code to your project:
|
||||
```nim
|
||||
type
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
|
||||
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
|
||||
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
|
||||
* Fix how `relativePath` handle case sensitiviy
|
||||
* Fix how `relativePath` handles case sensitivity
|
||||
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
|
||||
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
|
||||
* VM: no special casing for big endian machines
|
||||
@@ -43,7 +43,7 @@
|
||||
* threadpool: fix link in docs (#12258)
|
||||
* Fix spellings (#12277)
|
||||
* fix #12278, don't expose internal PCRE documentation
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fixed "Documentation of quitprocs is wrong" [#12279](https://github.com/nim-lang/Nim/issues/12279)
|
||||
* Fix typo in docs
|
||||
* Fix reference to parseSpec proc in readme
|
||||
* [doc/tut1] removed discard discussion in comments
|
||||
|
||||
@@ -169,7 +169,7 @@ echo f
|
||||
- The Nim compiler now supports a new pragma called ``.localPassc`` to
|
||||
pass specific compiler options to the C(++) backend for the C(++) file
|
||||
that was produced from the current Nim module.
|
||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
||||
- The compiler now infers "sink parameters". To disable this for a specific routine,
|
||||
annotate it with `.nosinks`. To disable it for a section of code, use
|
||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
||||
- The compiler now supports a new switch `--panics:on` that turns runtime
|
||||
@@ -261,7 +261,7 @@ echo f
|
||||
([#12812](https://github.com/nim-lang/Nim/issues/12812))
|
||||
- Fixed "Produce static/const initializations for variables when possible"
|
||||
([#12216](https://github.com/nim-lang/Nim/issues/12216))
|
||||
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
|
||||
- Fixed "Assigning discriminator field leads to internal assert with --gc:destructors"
|
||||
([#12821](https://github.com/nim-lang/Nim/issues/12821))
|
||||
- Fixed "nimsuggest `use` command does not return all instances of symbol"
|
||||
([#12832](https://github.com/nim-lang/Nim/issues/12832))
|
||||
|
||||
@@ -283,7 +283,7 @@ The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
This new definition is much easier to understand, the price is some expressiveness. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
@@ -279,7 +279,7 @@ const
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
|
||||
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
|
||||
tyAnd, tyOr, tyNot, tyAnything}
|
||||
|
||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
|
||||
@@ -447,6 +447,8 @@ const
|
||||
tfReturnsNew* = tfInheritable
|
||||
tfNonConstExpr* = tfExplicitCallConv
|
||||
## tyFromExpr where the expression shouldn't be evaluated as a static value
|
||||
tfGenericHasDestructor* = tfExplicitCallConv
|
||||
## tyGenericBody where an instance has a generated destructor
|
||||
skError* = skUnknown
|
||||
|
||||
var
|
||||
@@ -1645,7 +1647,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
if mask != {} and propagateHasAsgn:
|
||||
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if o2.kind in {tyTuple, tyObject, tyArray,
|
||||
tySequence, tySet, tyDistinct}:
|
||||
tySequence, tyString, tySet, tyDistinct}:
|
||||
o2.flags.incl mask
|
||||
owner.flags.incl mask
|
||||
|
||||
|
||||
121
compiler/cbuilder.nim
Normal file
121
compiler/cbuilder.nim
Normal file
@@ -0,0 +1,121 @@
|
||||
type
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet) =
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
|
||||
if tfPacked in typ.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(typ))
|
||||
else:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
type BaseClassKind = enum
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
var baseKind = bcNone
|
||||
if typ.kind == tyObject:
|
||||
if typ.baseClass == nil:
|
||||
if lacksMTypeField(typ):
|
||||
baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
baseKind = bcCppInherit
|
||||
else:
|
||||
baseKind = bcSupField
|
||||
if baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
let currLen = obj.len
|
||||
case baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if typ.itemId notin m.g.graph.memberProcsPerType and
|
||||
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
|
||||
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
body
|
||||
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
obj.add("};\n")
|
||||
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building it according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
@@ -1,6 +0,0 @@
|
||||
type
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
@@ -1,349 +0,0 @@
|
||||
type VarKind = enum
|
||||
Local
|
||||
Global
|
||||
Threadvar
|
||||
Const
|
||||
AlwaysConst ## const even on C++
|
||||
|
||||
proc addVarHeader(builder: var Builder, kind: VarKind) =
|
||||
## adds modifiers for given var kind:
|
||||
## Local has no modifier
|
||||
## Global has `static` modifier
|
||||
## Const has `static NIM_CONST` modifier
|
||||
## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
|
||||
## Threadvar is unimplemented
|
||||
case kind
|
||||
of Local: discard
|
||||
of Global:
|
||||
builder.add("static ")
|
||||
of Const:
|
||||
builder.add("static NIM_CONST ")
|
||||
of AlwaysConst:
|
||||
builder.add("static const ")
|
||||
of Threadvar:
|
||||
doAssert false, "unimplemented"
|
||||
|
||||
proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
|
||||
## adds a variable declaration to the builder
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.add(";\n")
|
||||
|
||||
template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
|
||||
## adds a variable declaration to the builder, with the `body` building the type
|
||||
builder.addVarHeader(kind)
|
||||
body
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(";\n")
|
||||
|
||||
template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typeBody, initializerBody: typed) =
|
||||
## adds a variable declaration to the builder, with `typeBody` building the type, and
|
||||
## `initializerBody` building the initializer. initializer must be provided
|
||||
builder.addVarHeader(kind)
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.add(";\n")
|
||||
|
||||
proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
|
||||
## adds an array variable declaration to the builder
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(elementType)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addInt(len)
|
||||
builder.add("]")
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.add(";\n")
|
||||
|
||||
template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
|
||||
## adds an array variable declaration to the builder with the initializer built according to `body`
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(elementType)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addInt(len)
|
||||
builder.add("] = ")
|
||||
body
|
||||
builder.add(";\n")
|
||||
|
||||
template addTypedef(builder: var Builder, name: string, typeBody: typed) =
|
||||
## adds a typedef declaration to the builder with name `name` and type as
|
||||
## built in `typeBody`
|
||||
builder.add("typedef ")
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(";\n")
|
||||
|
||||
template addArrayTypedef(builder: var Builder, name: string, len: int, typeBody: typed) =
|
||||
## adds an array typedef declaration to the builder with name `name`,
|
||||
## length `len`, and element type as built in `typeBody`
|
||||
builder.add("typedef ")
|
||||
typeBody
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add("[")
|
||||
builder.addInt(len)
|
||||
builder.add("];\n")
|
||||
|
||||
type
|
||||
StructInitializerKind = enum
|
||||
siOrderedStruct ## struct constructor, but without named fields on C
|
||||
siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
|
||||
siArray ## array constructor
|
||||
siWrapper ## wrapper for a single field, generates it verbatim
|
||||
|
||||
StructInitializer = object
|
||||
## context for building struct initializers, i.e. `{ field1, field2 }`
|
||||
kind: StructInitializerKind
|
||||
## if true, fields will not be named, instead values are placed in order
|
||||
needsComma: bool
|
||||
|
||||
proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
|
||||
## starts building a struct initializer, i.e. braced initializer list
|
||||
result = StructInitializer(kind: kind, needsComma: false)
|
||||
if kind != siWrapper:
|
||||
builder.add("{")
|
||||
|
||||
template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
|
||||
## adds a field to a struct initializer, with the value built in `valueBody`
|
||||
if constr.needsComma:
|
||||
assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
|
||||
builder.add(", ")
|
||||
else:
|
||||
constr.needsComma = true
|
||||
case constr.kind
|
||||
of siArray, siWrapper:
|
||||
# no name, can just add value
|
||||
valueBody
|
||||
of siOrderedStruct:
|
||||
# no name, can just add value on C
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
valueBody
|
||||
of siNamedStruct:
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
builder.add(".")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
|
||||
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
|
||||
## finishes building a struct initializer
|
||||
if constr.kind != siWrapper:
|
||||
builder.add("}")
|
||||
|
||||
template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
|
||||
## builds a struct initializer, i.e. `{ field1, field2 }`
|
||||
## a `var StructInitializer` must be declared and passed as a parameter so
|
||||
## that it can be used with `addField`
|
||||
constr = builder.initStructInitializer(kind)
|
||||
body
|
||||
builder.finishStructInitializer(constr)
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
|
||||
## adds a field inside a struct/union type
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
|
||||
## adds an array field inside a struct/union type
|
||||
obj.add('\t')
|
||||
obj.add(elementType)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add("[")
|
||||
obj.addInt(len)
|
||||
obj.add("]")
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
|
||||
## adds an field inside a struct/union type, based on an `skField` symbol
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
type
|
||||
BaseClassKind = enum
|
||||
## denotes how and whether or not the base class/RTTI should be stored
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
StructBuilderInfo = object
|
||||
## context for building `struct` types
|
||||
baseKind: BaseClassKind
|
||||
named: bool
|
||||
preFieldsLen: int
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
|
||||
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
|
||||
obj.add("struct")
|
||||
if result.named:
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if baseType.len != 0:
|
||||
if m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
if result.baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.len
|
||||
if result.baseKind == bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
|
||||
proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.len:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
obj.add("}")
|
||||
|
||||
template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
|
||||
## builds a struct type not based on a Nim type with fields according to `body`,
|
||||
## `name` can be empty to build as a type expression and not a statement
|
||||
let info = startSimpleStruct(obj, m, name, baseType)
|
||||
body
|
||||
finishSimpleStruct(obj, m, info)
|
||||
|
||||
proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
|
||||
result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
|
||||
if tfPacked in t.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(t))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(t))
|
||||
else:
|
||||
obj.add(structOrUnion(t))
|
||||
if result.named:
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if t.kind == tyObject:
|
||||
if t.baseClass == nil:
|
||||
if lacksMTypeField(t):
|
||||
result.baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
result.baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
result.baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
elif baseType.len != 0:
|
||||
if m.compileToCpp:
|
||||
result.baseKind = bcCppInherit
|
||||
else:
|
||||
result.baseKind = bcSupField
|
||||
if result.baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
result.preFieldsLen = obj.len
|
||||
case result.baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if t.itemId notin m.g.graph.memberProcsPerType and
|
||||
t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
|
||||
t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
|
||||
proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
|
||||
if info.baseKind == bcNone and info.preFieldsLen == obj.len and
|
||||
t.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
if info.named:
|
||||
obj.add("};\n")
|
||||
else:
|
||||
obj.add("}")
|
||||
if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
obj.add("#pragma pack(pop)\n")
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
|
||||
## builds a struct type directly based on `typ` with fields according to `body`,
|
||||
## `name` can be empty to build as a type expression and not a statement
|
||||
let info = startStruct(obj, m, typ, name, baseType)
|
||||
body
|
||||
finishStruct(obj, m, typ, info)
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building the fields according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
## adds an anonymous union i.e. `union { ... };` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
@@ -1,26 +0,0 @@
|
||||
# XXX make complex ones like bitOr use builder instead
|
||||
# XXX add stuff like NI, NIM_NIL as constants
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
const
|
||||
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
|
||||
"N_STDCALL", "N_CDECL", "N_SAFECALL",
|
||||
"N_SYSCALL", # this is probably not correct for all platforms,
|
||||
# but one can #define it to what one wants
|
||||
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
|
||||
"N_NOCONV" #ccMember is N_NOCONV
|
||||
]
|
||||
|
||||
proc procPtrType(conv: TCallingConvention, rettype: Snippet, name: string): Snippet =
|
||||
CallingConvToStr[conv] & "_PTR(" & rettype & ", " & name & ")"
|
||||
|
||||
proc cCast(typ, value: Snippet): Snippet =
|
||||
"((" & typ & ") " & value & ")"
|
||||
|
||||
proc cAddr(value: Snippet): Snippet =
|
||||
"&" & value
|
||||
|
||||
proc bitOr(a, b: Snippet): Snippet =
|
||||
"(" & a & " | " & b & ")"
|
||||
@@ -135,7 +135,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
d.snippet = pl
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone and p.splitDecls == 0:
|
||||
if d.k == locNone and p.splitDecls == 0 and p.config.exc != excGoto:
|
||||
d = getTempCpp(p, typ.returnType, pl)
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
|
||||
@@ -13,7 +13,7 @@ when defined(nimCompilerStacktraceHints):
|
||||
import std/stackframes
|
||||
|
||||
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
|
||||
result: var Builder; init: var StructInitializer;
|
||||
result: var Rope; count: var int;
|
||||
isConst: bool, info: TLineInfo)
|
||||
|
||||
# -------------------------- constant expressions ------------------------
|
||||
@@ -1572,6 +1572,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
discard getTypeDesc(p.module, t)
|
||||
let ty = getUniqueType(t)
|
||||
for i in 1..<e.len:
|
||||
if nfPreventCg in e[i].flags:
|
||||
# this is an object constructor node generated by the VM and
|
||||
# this field is in an inactive case branch, don't generate assignment
|
||||
continue
|
||||
var check: PNode = nil
|
||||
if e[i].len == 3 and optFieldCheck in p.options:
|
||||
check = e[i][2]
|
||||
@@ -2155,6 +2159,8 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
elif etyp.kind == tyBool and srcTyp.kind in IntegralTypes:
|
||||
putIntoDest(p, d, e, "(($1) != 0)" % [rdCharLoc(a)], a.storage)
|
||||
elif etyp.kind == tyProc and srcTyp.kind == tyProc and sameBackendType(etyp, srcTyp):
|
||||
expr(p, e[1], d)
|
||||
else:
|
||||
if etyp.kind == tyPtr:
|
||||
# generates the definition of structs for casts like cast[ptr object](addr x)[]
|
||||
@@ -3074,16 +3080,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkObjConstr: genObjConstr(p, n, d)
|
||||
of nkCast: genCast(p, n, d)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
|
||||
of nkHiddenAddr:
|
||||
if n[0].kind == nkDerefExpr:
|
||||
# addr ( deref ( x )) --> x
|
||||
var x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ() = n.typ
|
||||
expr(p, x, d)
|
||||
return
|
||||
genAddr(p, n, d)
|
||||
of nkAddr: genAddr(p, n, d)
|
||||
of nkAddr, nkHiddenAddr: genAddr(p, n, d)
|
||||
of nkBracketExpr: genBracketExpr(p, n, d)
|
||||
of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
|
||||
of nkDotExpr: genRecordField(p, n, d)
|
||||
@@ -3114,6 +3111,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkConstSection:
|
||||
if useAliveDataFromDce in p.module.flags:
|
||||
genConstStmt(p, n)
|
||||
else: # enforce addressable consts for exportc
|
||||
let m = p.module
|
||||
for it in n:
|
||||
let symNode = skipPragmaExpr(it[0])
|
||||
if symNode.kind == nkSym and sfExportc in symNode.sym.flags:
|
||||
requestConstImpl(p, symNode.sym)
|
||||
# else: consts generated lazily on use
|
||||
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
|
||||
of nkCaseStmt: genCase(p, n, d)
|
||||
@@ -3195,7 +3198,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkMixinStmt, nkBindStmt: discard
|
||||
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||
|
||||
proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder) =
|
||||
proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
|
||||
var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyBool: result.add rope"NIM_FALSE"
|
||||
@@ -3206,56 +3209,38 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder)
|
||||
result.add rope"NIM_NIL"
|
||||
of tyString, tySequence:
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
var seqInit: StructInitializer
|
||||
result.addStructInitializer(seqInit, kind = siOrderedStruct):
|
||||
result.addField(seqInit, name = "len"):
|
||||
result.add("0")
|
||||
result.addField(seqInit, name = "p"):
|
||||
result.add("NIM_NIL")
|
||||
result.add "{0, NIM_NIL}"
|
||||
else:
|
||||
result.add "NIM_NIL"
|
||||
of tyProc:
|
||||
if t.callConv != ccClosure:
|
||||
result.add "NIM_NIL"
|
||||
else:
|
||||
var closureInit: StructInitializer
|
||||
result.addStructInitializer(closureInit, kind = siOrderedStruct):
|
||||
result.addField(closureInit, name = "ClP_0"):
|
||||
result.add("NIM_NIL")
|
||||
result.addField(closureInit, name = "ClE_0"):
|
||||
result.add("NIM_NIL")
|
||||
result.add "{NIM_NIL, NIM_NIL}"
|
||||
of tyObject:
|
||||
var objInit: StructInitializer
|
||||
result.addStructInitializer(objInit, kind = siOrderedStruct):
|
||||
getNullValueAuxT(p, t, t, t.n, nil, result, objInit, true, info)
|
||||
var count = 0
|
||||
result.add "{"
|
||||
getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info)
|
||||
result.add "}"
|
||||
of tyTuple:
|
||||
var tupleInit: StructInitializer
|
||||
result.addStructInitializer(tupleInit, kind = siOrderedStruct):
|
||||
if p.vccAndC and t.isEmptyTupleType:
|
||||
result.addField(tupleInit, name = "dummy"):
|
||||
result.add "0"
|
||||
for i, a in t.ikids:
|
||||
result.addField(tupleInit, name = "Field" & $i):
|
||||
getDefaultValue(p, a, info, result)
|
||||
result.add "{"
|
||||
if p.vccAndC and t.isEmptyTupleType:
|
||||
result.add "0"
|
||||
for i, a in t.ikids:
|
||||
if i > 0: result.add ", "
|
||||
getDefaultValue(p, a, info, result)
|
||||
result.add "}"
|
||||
of tyArray:
|
||||
var arrInit: StructInitializer
|
||||
result.addStructInitializer(arrInit, kind = siArray):
|
||||
for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
|
||||
result.addField(arrInit, name = ""):
|
||||
getDefaultValue(p, t.elementType, info, result)
|
||||
result.add "{"
|
||||
for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
|
||||
if i > 0: result.add ", "
|
||||
getDefaultValue(p, t.elementType, info, result)
|
||||
result.add "}"
|
||||
#result = rope"{}"
|
||||
of tyOpenArray, tyVarargs:
|
||||
var openArrInit: StructInitializer
|
||||
result.addStructInitializer(openArrInit, kind = siOrderedStruct):
|
||||
result.addField(openArrInit, name = "Field0"):
|
||||
result.add("NIM_NIL")
|
||||
result.addField(openArrInit, name = "Field1"):
|
||||
result.add("0")
|
||||
result.add "{NIM_NIL, 0}"
|
||||
of tySet:
|
||||
if mapSetType(p.config, t) == ctArray:
|
||||
var setInit: StructInitializer
|
||||
result.addStructInitializer(setInit, kind = siArray):
|
||||
discard
|
||||
if mapSetType(p.config, t) == ctArray: result.add "{}"
|
||||
else: result.add "0"
|
||||
else:
|
||||
globalError(p.config, info, "cannot create null element for: " & $t.kind)
|
||||
@@ -3266,18 +3251,16 @@ proc isEmptyCaseObjectBranch(n: PNode): bool =
|
||||
return true
|
||||
|
||||
proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
result: var Builder; init: var StructInitializer;
|
||||
result: var Rope; count: var int;
|
||||
isConst: bool, info: TLineInfo) =
|
||||
case obj.kind
|
||||
of nkRecList:
|
||||
let isUnion = tfUnion in t.flags
|
||||
for it in obj.sons:
|
||||
getNullValueAux(p, t, it, constOrNil, result, init, isConst, info)
|
||||
if isUnion:
|
||||
# generate only 1 field for default value of union
|
||||
return
|
||||
getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
|
||||
of nkRecCase:
|
||||
getNullValueAux(p, t, obj[0], constOrNil, result, init, isConst, info)
|
||||
getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
|
||||
var res = ""
|
||||
if count > 0: res.add ", "
|
||||
var branch = Zero
|
||||
if constOrNil != nil:
|
||||
## find kind value, default is zero if not specified
|
||||
@@ -3291,186 +3274,140 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
break
|
||||
|
||||
let selectedBranch = caseObjDefaultBranch(obj, branch)
|
||||
res.add "{"
|
||||
var countB = 0
|
||||
let b = lastSon(obj[selectedBranch])
|
||||
# designated initilization is the only way to init non first element of unions
|
||||
# branches are allowed to have no members (b.len == 0), in this case they don't need initializer
|
||||
var fieldName: string = ""
|
||||
if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
|
||||
fieldName = "_" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch
|
||||
result.addField(init, name = "<anonymous union>"):
|
||||
# XXX figure out name for the union, see use of `addAnonUnion`
|
||||
var branchInit: StructInitializer
|
||||
result.addStructInitializer(branchInit, kind = siNamedStruct):
|
||||
result.addField(branchInit, name = fieldName):
|
||||
var branchObjInit: StructInitializer
|
||||
result.addStructInitializer(branchObjInit, kind = siOrderedStruct):
|
||||
getNullValueAux(p, t, b, constOrNil, result, branchObjInit, isConst, info)
|
||||
res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
|
||||
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
|
||||
res.add "}"
|
||||
elif b.kind == nkSym:
|
||||
fieldName = mangleRecFieldName(p.module, b.sym)
|
||||
result.addField(init, name = "<anonymous union>"):
|
||||
# XXX figure out name for the union, see use of `addAnonUnion`
|
||||
var branchInit: StructInitializer
|
||||
result.addStructInitializer(branchInit, kind = siNamedStruct):
|
||||
result.addField(branchInit, name = fieldName):
|
||||
# we need to generate the default value of the single sym,
|
||||
# to do this create a dummy wrapper initializer and recurse
|
||||
var branchFieldInit: StructInitializer
|
||||
result.addStructInitializer(branchFieldInit, kind = siWrapper):
|
||||
getNullValueAux(p, t, b, constOrNil, result, branchFieldInit, isConst, info)
|
||||
res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
|
||||
getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info)
|
||||
else:
|
||||
# no fields, don't initialize
|
||||
return
|
||||
result.add res
|
||||
result.add "}"
|
||||
|
||||
of nkSym:
|
||||
if count > 0: result.add ", "
|
||||
inc count
|
||||
let field = obj.sym
|
||||
let sname = mangleRecFieldName(p.module, field)
|
||||
result.addField(init, name = sname):
|
||||
block fieldInit:
|
||||
if constOrNil != nil:
|
||||
for i in 1..<constOrNil.len:
|
||||
if constOrNil[i].kind == nkExprColonExpr:
|
||||
assert constOrNil[i][0].kind == nkSym, "illformed object constr; the field is not a sym"
|
||||
if constOrNil[i][0].sym.name.id == field.name.id:
|
||||
genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
|
||||
break fieldInit
|
||||
elif i == field.position:
|
||||
genBracedInit(p, constOrNil[i], isConst, field.typ, result)
|
||||
break fieldInit
|
||||
# not found, produce default value:
|
||||
getDefaultValue(p, field.typ, info, result)
|
||||
if constOrNil != nil:
|
||||
for i in 1..<constOrNil.len:
|
||||
if constOrNil[i].kind == nkExprColonExpr:
|
||||
assert constOrNil[i][0].kind == nkSym, "illformed object constr; the field is not a sym"
|
||||
if constOrNil[i][0].sym.name.id == field.name.id:
|
||||
genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
|
||||
return
|
||||
elif i == field.position:
|
||||
genBracedInit(p, constOrNil[i], isConst, field.typ, result)
|
||||
return
|
||||
# not found, produce default value:
|
||||
getDefaultValue(p, field.typ, info, result)
|
||||
else:
|
||||
localError(p.config, info, "cannot create null element for: " & $obj)
|
||||
|
||||
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
|
||||
result: var Builder; init: var StructInitializer;
|
||||
result: var Rope; count: var int;
|
||||
isConst: bool, info: TLineInfo) =
|
||||
var base = t.baseClass
|
||||
when false:
|
||||
let oldRes = result
|
||||
let oldcount = count
|
||||
let oldRes = result
|
||||
let oldcount = count
|
||||
if base != nil:
|
||||
result.add "{"
|
||||
base = skipTypes(base, skipPtrs)
|
||||
result.addField(init, name = "Sup"):
|
||||
var baseInit: StructInitializer
|
||||
result.addStructInitializer(baseInit, kind = siOrderedStruct):
|
||||
getNullValueAuxT(p, orig, base, base.n, constOrNil, result, baseInit, isConst, info)
|
||||
getNullValueAuxT(p, orig, base, base.n, constOrNil, result, count, isConst, info)
|
||||
result.add "}"
|
||||
elif not isObjLackingTypeField(t):
|
||||
result.addField(init, name = "m_type"):
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
result.add genTypeInfoV2(p.module, orig, obj.info)
|
||||
else:
|
||||
result.add genTypeInfoV1(p.module, orig, obj.info)
|
||||
getNullValueAux(p, t, obj, constOrNil, result, init, isConst, info)
|
||||
when false: # referring to Sup field, hopefully not a problem
|
||||
# do not emit '{}' as that is not valid C:
|
||||
if oldcount == count: result = oldRes
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
result.add genTypeInfoV2(p.module, orig, obj.info)
|
||||
else:
|
||||
result.add genTypeInfoV1(p.module, orig, obj.info)
|
||||
inc count
|
||||
getNullValueAux(p, t, obj, constOrNil, result, count, isConst, info)
|
||||
# do not emit '{}' as that is not valid C:
|
||||
if oldcount == count: result = oldRes
|
||||
|
||||
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Builder) =
|
||||
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Rope) =
|
||||
let t = n.typ.skipTypes(abstractInstOwned)
|
||||
var count = 0
|
||||
#if not isObjLackingTypeField(t) and not p.module.compileToCpp:
|
||||
# result.addf("{$1}", [genTypeInfo(p.module, t)])
|
||||
# inc count
|
||||
var objInit: StructInitializer
|
||||
result.addStructInitializer(objInit, kind = siOrderedStruct):
|
||||
if t.kind == tyObject:
|
||||
getNullValueAuxT(p, t, t, t.n, n, result, objInit, isConst, n.info)
|
||||
result.add "{"
|
||||
if t.kind == tyObject:
|
||||
getNullValueAuxT(p, t, t, t.n, n, result, count, isConst, n.info)
|
||||
result.add("}\n")
|
||||
|
||||
proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Builder) =
|
||||
var arrInit: StructInitializer
|
||||
result.addStructInitializer(arrInit, kind = siArray):
|
||||
if p.vccAndC and n.len == 0 and n.typ.kind == tyArray:
|
||||
result.addField(arrInit, name = ""):
|
||||
getDefaultValue(p, n.typ.elementType, n.info, result)
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
var ind, val: PNode
|
||||
if it.kind == nkExprColonExpr:
|
||||
ind = it[0]
|
||||
val = it[1]
|
||||
else:
|
||||
ind = it
|
||||
val = it
|
||||
result.addField(arrInit, name = ""):
|
||||
genBracedInit(p, val, isConst, ind.typ, result)
|
||||
proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) =
|
||||
result.add "{"
|
||||
if p.vccAndC and n.len == 0 and n.typ.kind == tyArray:
|
||||
getDefaultValue(p, n.typ.elementType, n.info, result)
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
if i > 0: result.add ",\n"
|
||||
if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, it[0].typ, result)
|
||||
else: genBracedInit(p, it, isConst, it.typ, result)
|
||||
result.add("}\n")
|
||||
|
||||
proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Builder) =
|
||||
var tupleInit: StructInitializer
|
||||
result.addStructInitializer(tupleInit, kind = siOrderedStruct):
|
||||
if p.vccAndC and n.len == 0:
|
||||
result.addField(tupleInit, name = "dummy"):
|
||||
result.add("0")
|
||||
for i in 0..<n.len:
|
||||
var it = n[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
it = it[1]
|
||||
result.addField(tupleInit, name = "Field" & $i):
|
||||
genBracedInit(p, it, isConst, tup[i], result)
|
||||
proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) =
|
||||
result.add "{"
|
||||
if p.vccAndC and n.len == 0:
|
||||
result.add "0"
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
if i > 0: result.add ",\n"
|
||||
if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result)
|
||||
else: genBracedInit(p, it, isConst, tup[i], result)
|
||||
result.add("}\n")
|
||||
|
||||
proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builder) =
|
||||
proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
|
||||
var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope]
|
||||
let base = t.skipTypes(abstractInst)[0]
|
||||
if n.len > 0:
|
||||
# array part needs extra curlies:
|
||||
data.add(", {")
|
||||
for i in 0..<n.len:
|
||||
if i > 0: data.addf(",$n", [])
|
||||
genBracedInit(p, n[i], isConst, base, data)
|
||||
data.add("}")
|
||||
data.add("}")
|
||||
|
||||
let tmpName = getTempName(p.module)
|
||||
|
||||
var def = newBuilder("")
|
||||
def.addVarWithTypeAndInitializer(
|
||||
if isConst: Const else: Global,
|
||||
name = tmpName):
|
||||
def.addSimpleStruct(p.module, name = "", baseType = ""):
|
||||
def.addField(name = "sup", typ = cgsymValue(p.module, "TGenericSeq"))
|
||||
def.addArrayField(name = "data", elementType = getTypeDesc(p.module, base), len = n.len)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
def.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
def.addField(structInit, name = "sup"):
|
||||
var supInit: StructInitializer
|
||||
def.addStructInitializer(supInit, kind = siOrderedStruct):
|
||||
def.addField(supInit, name = "len"):
|
||||
def.add(n.len.rope)
|
||||
def.addField(supInit, name = "reserved"):
|
||||
def.add(bitOr(rope(n.len), "NIM_STRLIT_FLAG"))
|
||||
if n.len > 0:
|
||||
def.addField(structInit, name = "data"):
|
||||
var arrInit: StructInitializer
|
||||
def.addStructInitializer(arrInit, kind = siArray):
|
||||
for i in 0..<n.len:
|
||||
def.addField(arrInit, name = ""):
|
||||
genBracedInit(p, n[i], isConst, base, def)
|
||||
p.module.s[cfsStrData].add def
|
||||
appcg(p.module, cfsStrData,
|
||||
"static $5 struct {$n" &
|
||||
" #TGenericSeq Sup;$n" &
|
||||
" $1 data[$2];$n" &
|
||||
"} $3 = $4;$n", [
|
||||
getTypeDesc(p.module, base), n.len, tmpName, data,
|
||||
if isConst: "NIM_CONST" else: ""])
|
||||
|
||||
result.add cCast(typ = getTypeDesc(p.module, t), value = cAddr(tmpName))
|
||||
result.add "(($1)&$2)" % [getTypeDesc(p.module, t), tmpName]
|
||||
|
||||
proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Builder) =
|
||||
proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
|
||||
let base = t.skipTypes(abstractInst)[0]
|
||||
var data = rope""
|
||||
if n.len > 0:
|
||||
data.add(", {")
|
||||
for i in 0..<n.len:
|
||||
if i > 0: data.addf(",$n", [])
|
||||
genBracedInit(p, n[i], isConst, base, data)
|
||||
data.add("}")
|
||||
|
||||
let payload = getTempName(p.module)
|
||||
appcg(p.module, cfsStrData,
|
||||
"static $5 struct {$n" &
|
||||
" NI cap; $1 data[$2];$n" &
|
||||
"} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [
|
||||
getTypeDesc(p.module, base), n.len, payload, data,
|
||||
if isConst: "const" else: ""])
|
||||
result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload]
|
||||
|
||||
var def = newBuilder("")
|
||||
def.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = payload):
|
||||
def.addSimpleStruct(p.module, name = "", baseType = ""):
|
||||
def.addField(name = "cap", typ = "NI")
|
||||
def.addArrayField(name = "data", elementType = getTypeDesc(p.module, base), len = n.len)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
def.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
def.addField(structInit, name = "cap"):
|
||||
def.add(bitOr(rope(n.len), "NIM_STRLIT_FLAG"))
|
||||
if n.len > 0:
|
||||
def.addField(structInit, name = "data"):
|
||||
var arrInit: StructInitializer
|
||||
def.addStructInitializer(arrInit, kind = siArray):
|
||||
for i in 0..<n.len:
|
||||
def.addField(arrInit, name = ""):
|
||||
genBracedInit(p, n[i], isConst, base, def)
|
||||
p.module.s[cfsStrData].add def
|
||||
|
||||
var resultInit: StructInitializer
|
||||
result.addStructInitializer(resultInit, kind = siOrderedStruct):
|
||||
result.addField(resultInit, name = "len"):
|
||||
result.add(rope(n.len))
|
||||
result.addField(resultInit, name = "p"):
|
||||
result.add cCast(typ = ptrType(getSeqPayloadType(p.module, t)), value = cAddr(payload))
|
||||
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder) =
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) =
|
||||
case n.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
genBracedInit(p, n[1], isConst, n.typ, result)
|
||||
@@ -3505,16 +3442,11 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
|
||||
# in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
|
||||
# leading to duplicate code like this:
|
||||
# "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
|
||||
var closureInit: StructInitializer
|
||||
result.addStructInitializer(closureInit, kind = siOrderedStruct):
|
||||
result.addField(closureInit, name = "ClP_0"):
|
||||
if n[0].kind == nkNilLit:
|
||||
result.add("NIM_NIL")
|
||||
else:
|
||||
var d: TLoc = initLocExpr(p, n[0])
|
||||
result.add(cCast(typ = getClosureType(p.module, typ, clHalfWithEnv), value = rdLoc(d)))
|
||||
result.addField(closureInit, name = "ClE_0"):
|
||||
result.add("NIM_NIL")
|
||||
if n[0].kind == nkNilLit:
|
||||
result.add "{NIM_NIL,NIM_NIL}"
|
||||
else:
|
||||
var d: TLoc = initLocExpr(p, n[0])
|
||||
result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)]
|
||||
else:
|
||||
var d: TLoc = initLocExpr(p, n)
|
||||
result.add rdLoc(d)
|
||||
@@ -3526,21 +3458,17 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
|
||||
if n.kind != nkBracket:
|
||||
internalError(p.config, n.info, "const openArray expression is not an array construction")
|
||||
|
||||
var data = newRopeAppender()
|
||||
genConstSimpleList(p, n, isConst, data)
|
||||
|
||||
let payload = getTempName(p.module)
|
||||
let ctype = getTypeDesc(p.module, typ.elementType)
|
||||
let arrLen = n.len
|
||||
var data = newBuilder("")
|
||||
data.addArrayVarWithInitializer(
|
||||
kind = if isConst: AlwaysConst else: Global,
|
||||
name = payload, elementType = ctype, len = arrLen):
|
||||
genConstSimpleList(p, n, isConst, data)
|
||||
p.module.s[cfsStrData].add(data)
|
||||
var openArrInit: StructInitializer
|
||||
result.addStructInitializer(openArrInit, kind = siOrderedStruct):
|
||||
result.addField(openArrInit, name = "Field0"):
|
||||
result.add(cCast(typ = ptrType(ctype), value = cAddr(payload)))
|
||||
result.addField(openArrInit, name = "Field1"):
|
||||
result.add(rope arrLen)
|
||||
appcg(p.module, cfsStrData,
|
||||
"static $5 $1 $3[$2] = $4;$n", [
|
||||
ctype, arrLen, payload, data,
|
||||
if isConst: "const" else: ""])
|
||||
result.add "{($1*)&$2, $3}" % [ctype, payload, rope arrLen]
|
||||
|
||||
of tyObject:
|
||||
genConstObjConstr(p, n, isConst, result)
|
||||
|
||||
@@ -36,83 +36,50 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
cgsym(m, "TGenericSeq")
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "Sup", typ = "TGenericSeq")
|
||||
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "Sup"):
|
||||
var seqInit: StructInitializer
|
||||
res.addStructInitializer(seqInit, kind = siOrderedStruct):
|
||||
res.addField(seqInit, name = "len"):
|
||||
res.add(rope(s.len))
|
||||
res.addField(seqInit, name = "reserved"):
|
||||
res.add(cCast("NI", bitOr(cCast("NU", rope(s.len)), "NIM_STRLIT_FLAG")))
|
||||
res.addField(strInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(res)
|
||||
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
|
||||
[tmp, makeCString(s), rope(s.len)])
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
||||
if s.isNil:
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
else:
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var name: string = ""
|
||||
if id == m.labels:
|
||||
# string literal not found in the cache:
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, name)
|
||||
appcg(m, result, "((#NimStringDesc*) &", [])
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, result)
|
||||
result.add ")"
|
||||
else:
|
||||
name = m.tmpBase & $id
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name)))
|
||||
appcg(m, result, "((#NimStringDesc*) &$1$2)",
|
||||
[m.tmpBase, id])
|
||||
|
||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = result):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "cap", typ = "NI")
|
||||
res.addArrayField(name = "data", elementType = "NIM_CHAR", len = s.len + 1)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
res.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
res.addField(structInit, name = "cap"):
|
||||
res.add(bitOr(rope(s.len), "NIM_STRLIT_FLAG"))
|
||||
res.addField(structInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(res)
|
||||
m.s[cfsStrData].addf("static $4 struct {$n" &
|
||||
" NI cap; NIM_CHAR data[$2+1];$n" &
|
||||
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
|
||||
[result, rope(s.len), makeCString(s),
|
||||
rope(if isConst: "const" else: "")])
|
||||
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var litName: string
|
||||
if id == m.labels:
|
||||
let pureLit = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
cgsym(m, "NimStrPayload")
|
||||
cgsym(m, "NimStringV2")
|
||||
# string literal not found in the cache:
|
||||
litName = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst)
|
||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
||||
else:
|
||||
litName = m.tmpBase & $id
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp):
|
||||
res.add("NimStringV2")
|
||||
do:
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "len"):
|
||||
res.add(rope(n.strVal.len))
|
||||
res.addField(strInit, name = "p"):
|
||||
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
|
||||
m.s[cfsStrData].add(res)
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
|
||||
rope(if isConst: "const" else: "")])
|
||||
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
@@ -125,12 +92,7 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Ro
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
else:
|
||||
pureLit = m.tmpBase & rope(id)
|
||||
var strInit: StructInitializer
|
||||
result.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
result.addField(strInit, name = "len"):
|
||||
result.add(rope(n.strVal.len))
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
@@ -146,7 +108,7 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), "NIM_NIL"))
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
||||
case detectStrVersion(m)
|
||||
|
||||
@@ -279,7 +279,7 @@ proc genGotoVar(p: BProc; value: PNode) =
|
||||
else:
|
||||
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Builder)
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
|
||||
|
||||
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
|
||||
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
|
||||
|
||||
@@ -47,11 +47,7 @@ proc generateThreadLocalStorage(m: BModule) =
|
||||
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
||||
finishTypeDescriptions(m)
|
||||
var typedef = newBuilder("")
|
||||
typedef.addTypedef(name = "NimThreadVars"):
|
||||
typedef.addSimpleStruct(m, name = "", baseType = ""):
|
||||
typedef.add(m.g.nimtv)
|
||||
m.s[cfsSeqTypes].add(typedef)
|
||||
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv != "":
|
||||
|
||||
@@ -81,7 +81,6 @@ proc fillBackendName(m: BModule; s: PSym) =
|
||||
result.add '_'
|
||||
result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
|
||||
s.loc.snippet = result
|
||||
writeMangledName(m.ndi, s, m.config)
|
||||
|
||||
proc fillParamName(m: BModule; s: PSym) =
|
||||
if s.loc.snippet == "":
|
||||
@@ -105,7 +104,6 @@ proc fillParamName(m: BModule; s: PSym) =
|
||||
# That would lead to either needing to reload `proxy` or to overwrite the
|
||||
# executable file for the main module, which is running (or both!) -> error.
|
||||
s.loc.snippet = res.rope
|
||||
writeMangledName(m.ndi, s, m.config)
|
||||
|
||||
proc fillLocalName(p: BProc; s: PSym) =
|
||||
assert s.kind in skLocalVars+{skTemp}
|
||||
@@ -121,7 +119,6 @@ proc fillLocalName(p: BProc; s: PSym) =
|
||||
result.add "_" & rope(counter+1)
|
||||
p.sigConflicts.inc(key)
|
||||
s.loc.snippet = result
|
||||
if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
|
||||
|
||||
proc scopeMangledParam(p: BProc; param: PSym) =
|
||||
## parameter generation only takes BModule, not a BProc, so we have to
|
||||
@@ -285,6 +282,15 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
|
||||
|
||||
else: result = false
|
||||
|
||||
const
|
||||
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
|
||||
"N_STDCALL", "N_CDECL", "N_SAFECALL",
|
||||
"N_SYSCALL", # this is probably not correct for all platforms,
|
||||
# but one can #define it to what one wants
|
||||
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
|
||||
"N_NOCONV" #ccMember is N_NOCONV
|
||||
]
|
||||
|
||||
proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
|
||||
# returns nil if we need to declare this type
|
||||
# since types are now unique via the ``getUniqueType`` mechanism, this slow
|
||||
@@ -373,7 +379,6 @@ proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
|
||||
if result == "": result = cacheGetType(m.typeCache, sig)
|
||||
|
||||
proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
|
||||
# XXX should be no-op in NIFC
|
||||
if m.compileToCpp:
|
||||
m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
|
||||
else:
|
||||
@@ -430,11 +435,10 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TypeDescKind
|
||||
if cacheGetType(m.typeCache, sig) == "":
|
||||
m.typeCache[sig] = result
|
||||
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
|
||||
var struct = newBuilder("")
|
||||
struct.addSimpleStruct(m, name = result, baseType = ""):
|
||||
struct.addField(name = "len", typ = "NI")
|
||||
struct.addField(name = "p", typ = ptrType(result & "_Content"))
|
||||
m.s[cfsTypes].add(struct)
|
||||
appcg(m, m.s[cfsTypes],
|
||||
"struct $1 {\n" &
|
||||
" NI len; $1_Content* p;\n" &
|
||||
"};\n", [result])
|
||||
pushType(m, t)
|
||||
else:
|
||||
result = getTypeForward(m, t, sig) & seqStar(m)
|
||||
@@ -453,13 +457,9 @@ proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
|
||||
if result == "":
|
||||
discard getTypeDescAux(m, t, check, dkVar)
|
||||
else:
|
||||
var struct = newBuilder("")
|
||||
struct.addSimpleStruct(m, name = result & "_Content", baseType = ""):
|
||||
struct.addField(name = "cap", typ = "NI")
|
||||
struct.addField(name = "data",
|
||||
typ = getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar),
|
||||
isFlexArray = true)
|
||||
m.s[cfsTypes].add(struct)
|
||||
appcg(m, m.s[cfsTypes], """
|
||||
struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE]; };
|
||||
""", [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar), result])
|
||||
|
||||
proc paramStorageLoc(param: PSym): TStorageLoc =
|
||||
if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
|
||||
@@ -722,7 +722,6 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
|
||||
if unionBody.len != 0:
|
||||
result.addAnonUnion:
|
||||
# XXX this has to be a named field for NIFC
|
||||
result.add(unionBody)
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
@@ -847,12 +846,8 @@ proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKin
|
||||
result = getTypeName(m, t, sig)
|
||||
m.typeCache[sig] = result
|
||||
let elemType = getTypeDescWeak(m, t.elementType, check, kind)
|
||||
var typedef = newBuilder("")
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.addSimpleStruct(m, name = "", baseType = ""):
|
||||
typedef.addField(name = "Field0", typ = ptrType(elemType))
|
||||
typedef.addField(name = "Field1", typ = "NI")
|
||||
m.s[cfsTypes].add(typedef)
|
||||
m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n",
|
||||
[result, elemType])
|
||||
|
||||
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
|
||||
# returns only the type's name
|
||||
@@ -924,28 +919,17 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
(sfImportc in t.sym.flags and t.sym.magic == mNone)):
|
||||
m.typeCache[sig] = result
|
||||
var size: int
|
||||
var typedef = newBuilder("")
|
||||
if firstOrd(m.config, t) < 0:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.add("NI32")
|
||||
m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
|
||||
size = 4
|
||||
else:
|
||||
size = int(getSize(m.config, t))
|
||||
case size
|
||||
of 1:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.add("NU8")
|
||||
of 2:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.add("NU16")
|
||||
of 4:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.add("NI32")
|
||||
of 8:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.add("NI64")
|
||||
of 1: m.s[cfsTypes].addf("typedef NU8 $1;$n", [result])
|
||||
of 2: m.s[cfsTypes].addf("typedef NU16 $1;$n", [result])
|
||||
of 4: m.s[cfsTypes].addf("typedef NI32 $1;$n", [result])
|
||||
of 8: m.s[cfsTypes].addf("typedef NI64 $1;$n", [result])
|
||||
else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
|
||||
m.s[cfsTypes].add(typedef)
|
||||
when false:
|
||||
let owner = hashOwner(t.sym)
|
||||
if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
|
||||
@@ -962,17 +946,14 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
var rettype, desc: Rope = ""
|
||||
genProcParams(m, t, rettype, desc, check, true, true)
|
||||
if not isImportedType(t):
|
||||
var typedef = newBuilder("")
|
||||
if t.callConv != ccClosure: # procedure vars may need a closure!
|
||||
typedef.addTypedef(name = desc):
|
||||
typedef.add(procPtrType(t.callConv, rettype = rettype, name = result))
|
||||
m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
|
||||
[rope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
||||
else:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.addSimpleStruct(m, name = "", baseType = ""):
|
||||
typedef.addField(name = desc, typ =
|
||||
procPtrType(ccNimCall, rettype = rettype, name = "ClP_0"))
|
||||
typedef.addField(name = "ClE_0", typ = "void*")
|
||||
m.s[cfsTypes].add(typedef)
|
||||
m.s[cfsTypes].addf("typedef struct {$n" &
|
||||
"N_NIMCALL_PTR($2, ClP_0) $3;$n" &
|
||||
"void* ClE_0;$n} $1;$n",
|
||||
[result, rettype, desc])
|
||||
of tySequence:
|
||||
if optSeqDestructors in m.config.globalOptions:
|
||||
result = getTypeDescWeak(m, t, check, kind)
|
||||
@@ -989,14 +970,18 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
m.typeCache[sig] = result & seqStar(m)
|
||||
if not isImportedType(t):
|
||||
if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
|
||||
var struct = newBuilder("")
|
||||
let baseType = cgsymValue(m, "TGenericSeq")
|
||||
struct.addSimpleStruct(m, name = result, baseType = baseType):
|
||||
struct.addField(
|
||||
name = "data",
|
||||
typ = getTypeDescAux(m, t.elementType, check, kind),
|
||||
isFlexArray = true)
|
||||
m.s[cfsSeqTypes].add struct
|
||||
const
|
||||
cppSeq = "struct $2 : #TGenericSeq {$n"
|
||||
cSeq = "struct $2 {$n" &
|
||||
" #TGenericSeq Sup;$n"
|
||||
if m.compileToCpp:
|
||||
appcg(m, m.s[cfsSeqTypes],
|
||||
cppSeq & " $1 data[SEQ_DECL_SIZE];$n" &
|
||||
"};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
|
||||
else:
|
||||
appcg(m, m.s[cfsSeqTypes],
|
||||
cSeq & " $1 data[SEQ_DECL_SIZE];$n" &
|
||||
"};$n", [getTypeDescAux(m, t.elementType, check, kind), result])
|
||||
else:
|
||||
result = rope("TGenericSeq")
|
||||
result.add(seqStar(m))
|
||||
@@ -1005,10 +990,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
m.typeCache[sig] = result
|
||||
if not isImportedType(t):
|
||||
let foo = getTypeDescAux(m, t.elementType, check, kind)
|
||||
var typedef = newBuilder("")
|
||||
typedef.addArrayTypedef(name = result, len = 1):
|
||||
typedef.add(foo)
|
||||
m.s[cfsTypes].add(typedef)
|
||||
m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result])
|
||||
of tyArray:
|
||||
var n: BiggestInt = toInt64(lengthOrd(m.config, t))
|
||||
if n <= 0: n = 1 # make an array of at least one element
|
||||
@@ -1016,10 +998,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
m.typeCache[sig] = result
|
||||
if not isImportedType(t):
|
||||
let e = getTypeDescAux(m, t.elementType, check, kind)
|
||||
var typedef = newBuilder("")
|
||||
typedef.addArrayTypedef(name = result, len = n):
|
||||
typedef.add(e)
|
||||
m.s[cfsTypes].add(typedef)
|
||||
m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
|
||||
[e, result, rope(n)])
|
||||
of tyObject, tyTuple:
|
||||
let tt = origTyp.skipTypes({tyDistinct})
|
||||
if isImportedCppType(t) and tt.kind == tyGenericInst:
|
||||
@@ -1065,8 +1045,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
# with the C macros for defining procs such as N_NIMCALL. We must
|
||||
# create a typedef for the type and use it in the proc signature:
|
||||
let typedefName = "TY" & $sig
|
||||
m.s[cfsTypes].addTypedef(name = typedefName):
|
||||
m.s[cfsTypes].add(result)
|
||||
m.s[cfsTypes].addf("typedef $1 $2;$n", [result, typedefName])
|
||||
m.typeCache[sig] = typedefName
|
||||
result = typedefName
|
||||
else:
|
||||
@@ -1094,12 +1073,9 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
if not isImportedType(t):
|
||||
let s = int(getSize(m.config, t))
|
||||
case s
|
||||
of 1, 2, 4, 8:
|
||||
m.s[cfsTypes].addTypedef(name = result):
|
||||
m.s[cfsTypes].add("NU" & rope(s*8))
|
||||
else:
|
||||
m.s[cfsTypes].addArrayTypedef(name = result, len = s):
|
||||
m.s[cfsTypes].add("NU8")
|
||||
of 1, 2, 4, 8: m.s[cfsTypes].addf("typedef NU$2 $1;$n", [result, rope(s*8)])
|
||||
else: m.s[cfsTypes].addf("typedef NU8 $1[$2];$n",
|
||||
[result, rope(getSize(m.config, t))])
|
||||
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyInferred:
|
||||
result = getTypeDescAux(m, skipModifier(t), check, kind)
|
||||
@@ -1127,17 +1103,14 @@ proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope =
|
||||
var rettype, desc: Rope = ""
|
||||
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
|
||||
if not isImportedType(t):
|
||||
var typedef = newBuilder("")
|
||||
if t.callConv != ccClosure or kind != clFull:
|
||||
typedef.addTypedef(name = desc):
|
||||
typedef.add(procPtrType(t.callConv, rettype = rettype, name = result))
|
||||
m.s[cfsTypes].addf("typedef $1_PTR($2, $3) $4;$n",
|
||||
[rope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
||||
else:
|
||||
typedef.addTypedef(name = result):
|
||||
typedef.addSimpleStruct(m, name = "", baseType = ""):
|
||||
typedef.addField(name = desc, typ =
|
||||
procPtrType(ccNimCall, rettype = rettype, name = "ClP_0"))
|
||||
typedef.addField(name = "ClE_0", typ = "void*")
|
||||
m.s[cfsTypes].add(typedef)
|
||||
m.s[cfsTypes].addf("typedef struct {$n" &
|
||||
"N_NIMCALL_PTR($2, ClP_0) $3;$n" &
|
||||
"void* ClE_0;$n} $1;$n",
|
||||
[result, rettype, desc])
|
||||
|
||||
proc finishTypeDescriptions(m: BModule) =
|
||||
var i = 0
|
||||
|
||||
@@ -14,7 +14,7 @@ import
|
||||
nversion, nimsets, msgs, bitsets, idents, types,
|
||||
ccgutils, ropes, wordrecg, treetab, cgmeth,
|
||||
rodutils, renderer, cgendata, aliases,
|
||||
lowerings, ndi, lineinfos, pathutils, transf,
|
||||
lowerings, lineinfos, pathutils, transf,
|
||||
injectdestructors, astmsgs, modulepaths, pushpoppragmas,
|
||||
mangleutils
|
||||
|
||||
@@ -373,9 +373,7 @@ proc dataField(p: BProc): Rope =
|
||||
|
||||
proc genProcPrototype(m: BModule, sym: PSym)
|
||||
|
||||
include cbuilderbase
|
||||
include cbuilderexprs
|
||||
include cbuilderdecls
|
||||
include cbuilder
|
||||
include ccgliterals
|
||||
include ccgtypes
|
||||
|
||||
@@ -2087,9 +2085,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
|
||||
if sfSystemModule in module.flags:
|
||||
incl result.flags, preventStackTrace
|
||||
excl(result.preInitProc.options, optStackTrace)
|
||||
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
|
||||
else: AbsoluteFile""
|
||||
open(result.ndi, ndiName, g.config)
|
||||
|
||||
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
|
||||
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
|
||||
@@ -2219,7 +2214,6 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
# it would generate multiple 'main' procs, for instance.
|
||||
|
||||
proc writeModule(m: BModule, pending: bool) =
|
||||
template onExit() = close(m.ndi, m.config)
|
||||
let cfile = getCFile(m)
|
||||
if moduleHasChanged(m.g.graph, m.module):
|
||||
genInitCode(m)
|
||||
@@ -2237,12 +2231,10 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
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()
|
||||
|
||||
proc updateCachedModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
@@ -2254,12 +2246,13 @@ proc updateCachedModule(m: BModule) =
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
|
||||
let procname = getIdent(graph.cache, "NimDestroyGlobals")
|
||||
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
|
||||
let procname = getIdent(graph.cache, prefixedName)
|
||||
result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
|
||||
result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
|
||||
result.typ.callConv = ccCDecl
|
||||
incl result.flags, sfExportc
|
||||
result.loc.snippet = "NimDestroyGlobals"
|
||||
result.loc.snippet = prefixedName
|
||||
if isDynlib:
|
||||
incl(result.loc.flags, lfExportLib)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
ndi, lineinfos, pathutils, modulegraphs
|
||||
lineinfos, pathutils, modulegraphs
|
||||
|
||||
import std/[intsets, tables, sets]
|
||||
|
||||
@@ -172,7 +172,6 @@ type
|
||||
# OpenGL wrapper
|
||||
sigConflicts*: CountTable[SigHash]
|
||||
g*: BModuleList
|
||||
ndi*: NdiFile
|
||||
|
||||
template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
|
||||
@@ -203,13 +203,14 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
else: invalidCmdLineOption(conf, pass, orig, info)
|
||||
|
||||
let isSomeHint = state in {wHint, wHintAsError}
|
||||
let isSomeWarning = state in {wWarning, wWarningAsError}
|
||||
template findNote(noteMin, noteMax, name) =
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else:
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
if isSomeHint or isSomeWarning:
|
||||
message(conf, info, warnUnknownNotes, "unknown $#: $#" % [name, id])
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
@@ -919,6 +920,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
|
||||
conf.maxLoopIterationsVM = value
|
||||
of "maxcalldepthvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
var value: int = 2_000
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
if value <= 0: localError(conf, info, "maxCallDepthVM must be a positive integer greater than zero")
|
||||
conf.maxCallDepthVM = value
|
||||
of "errormax":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
# Note: `nim check` (etc) can overwrite this.
|
||||
|
||||
@@ -78,6 +78,7 @@ type
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
@@ -88,22 +89,52 @@ proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool
|
||||
|
||||
proc acceptsAllTypes(t: PType): bool=
|
||||
result = false
|
||||
if t.kind == tyAnything:
|
||||
result = true
|
||||
elif t.kind == tyGenericParam:
|
||||
if tfImplicitTypeParam in t.flags:
|
||||
result = true
|
||||
if not t.hasElementType or t.elementType.kind == tyNone:
|
||||
result = true
|
||||
|
||||
proc matchKids(c: PContext; f, a: PType; m: var MatchCon, start=0): bool=
|
||||
result = true
|
||||
for i in start..<f.kidsLen - ord(f.kind == tyGenericInst):
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
|
||||
proc matchType(c: PContext; f, ao: 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}
|
||||
|
||||
var a = ao
|
||||
|
||||
case a.kind
|
||||
of tyGenericParam:
|
||||
let binding = m.existingBinding(a)
|
||||
if binding != nil:
|
||||
a = binding
|
||||
else:
|
||||
discard
|
||||
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.skipModifier, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
#let oldLen = m.inferred.len
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
|
||||
#m.inferred.setLen oldLen
|
||||
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
result = false
|
||||
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
|
||||
m.inferred.add((a, last m.potentialImplementation))
|
||||
result = true
|
||||
else:
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
else:
|
||||
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
|
||||
if f.hasElementType:
|
||||
@@ -112,14 +143,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = true # both lack it
|
||||
else:
|
||||
result = false
|
||||
|
||||
of tyGenericInvocation:
|
||||
result = false
|
||||
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
|
||||
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
|
||||
for i in FirstGenericParamAt ..< f.kidsLen:
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
return true
|
||||
result = matchKids(c, f, a, m, start=FirstGenericParamAt)
|
||||
of tyGenericParam:
|
||||
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
|
||||
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
|
||||
@@ -136,7 +164,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
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
|
||||
when logBindings: echo "B adding ", f, " ", last ak
|
||||
m.inferred.add((f, last ak))
|
||||
result = true
|
||||
else:
|
||||
@@ -151,7 +179,6 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
else:
|
||||
result = false
|
||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
||||
|
||||
of tyVar, tySink, tyLent, tyOwned:
|
||||
# modifiers in the concept must be there in the actual implementation
|
||||
# too but not vice versa.
|
||||
@@ -170,22 +197,43 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyConcept:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
if a.kind == tyConcept and f.n == a.n:
|
||||
result = true
|
||||
elif m.concpt.size == szIllegalRecursion:
|
||||
result = false
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
m.concpt.size = szIllegalRecursion
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = f
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.concpt.size = szUnknownSize
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyGenericBody:
|
||||
var ak = a
|
||||
if a.kind == tyGenericBody:
|
||||
ak = last(a)
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyCompositeTypeClass:
|
||||
var ak = if a.kind == tyCompositeTypeClass: a.last else: a
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
||||
tyGenericInst:
|
||||
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
for i in 0..<ak.kidsLen:
|
||||
if not matchType(c, f[i], ak[i], m): return false
|
||||
return true
|
||||
if f.kind == tyArray and f.kidsLen == 3:
|
||||
# XXX: this is a work-around!
|
||||
# system.nim creates these for the magic array typeclass
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
result = matchKids(c, f, ak, m)
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
@@ -221,6 +269,16 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = true
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
of tyStatic:
|
||||
result = false
|
||||
var scomp = f.base
|
||||
if scomp.kind == tyGenericParam:
|
||||
if f.base.kidsLen > 0:
|
||||
scomp = scomp.base
|
||||
if a.kind == tyStatic:
|
||||
result = matchType(c, scomp, a.base, m)
|
||||
else:
|
||||
result = matchType(c, scomp, a, m)
|
||||
else:
|
||||
result = false
|
||||
|
||||
@@ -271,7 +329,8 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
|
||||
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
|
||||
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
@@ -318,7 +377,9 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
|
||||
## `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)
|
||||
if arg.containsUnresolvedType:
|
||||
return false
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg, concpt: concpt)
|
||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
||||
if result:
|
||||
for (a, b) in m.inferred:
|
||||
|
||||
@@ -231,7 +231,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, trackUnusedImport: bool): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
|
||||
@@ -246,8 +246,10 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
let moduleIdent = if n.kind == nkInfix: n[^1] else: n
|
||||
c.unusedImports.add((result, moduleIdent.info))
|
||||
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
|
||||
result.info = moduleIdent.info
|
||||
if trackUnusedImport:
|
||||
c.unusedImports.add((result, result.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
@@ -288,10 +290,11 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
toFullPath(c.config, c.graph.importStack[i+1])
|
||||
c.recursiveDep = err
|
||||
|
||||
let trackUnusedImport = warnUnusedImportX in c.config.notes
|
||||
var realModule: PSym
|
||||
discard pushOptionEntry(c)
|
||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
||||
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
|
||||
popOptionEntry(c)
|
||||
|
||||
#echo "set back to ", L
|
||||
@@ -335,7 +338,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
||||
let m = myImportModule(c, it, importStmtResult)
|
||||
if m != nil:
|
||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
importAllSymbols(c, m)
|
||||
#importForwarded(c, m.ast, emptySet, m)
|
||||
afterImport(c, m)
|
||||
@@ -372,7 +375,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
|
||||
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
|
||||
for i in 1..<n.len:
|
||||
@@ -387,7 +390,7 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||
var m = myImportModule(c, n[0], result)
|
||||
if m != nil:
|
||||
n[0] = newSymNode(m)
|
||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
||||
addDecl(c, m) # add symbol to symbol table of module
|
||||
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
||||
#importForwarded(c, m.ast, exceptSet, m)
|
||||
afterImport(c, m)
|
||||
|
||||
@@ -802,15 +802,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = passCopyToSink(n, c, s)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
|
||||
nkCallKinds + nkLiterals:
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
if n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
@@ -886,12 +878,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
return
|
||||
|
||||
let inSpawn = c.inSpawn
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
|
||||
c.inSpawn.inc
|
||||
@@ -909,13 +895,19 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
isDangerous = true
|
||||
|
||||
result = shallowCopy(n)
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
if i < L and isCompileTimeOnly(parameters[i]):
|
||||
result[i] = n[i]
|
||||
elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0):
|
||||
result[i] = p(n[i], c, s, sinkArg)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
|
||||
when false:
|
||||
if isDangerous:
|
||||
|
||||
@@ -166,7 +166,7 @@ proc containsVariable(n: PNode): bool =
|
||||
proc checkIsolate*(n: PNode): bool =
|
||||
if types.containsTyRef(n.typ):
|
||||
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
||||
# worse than the already exisiting inheritance and closure restrictions.
|
||||
# worse than the already existing inheritance and closure restrictions.
|
||||
case n.kind
|
||||
of nkCharLit..nkNilLit:
|
||||
result = true
|
||||
|
||||
@@ -1585,15 +1585,8 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
|
||||
of nkObjDownConv:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenDeref:
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
gen(p, n[0], r)
|
||||
of nkDerefExpr:
|
||||
var x = n[0]
|
||||
if n.kind == nkHiddenAddr:
|
||||
x = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
x.typ() = n.typ
|
||||
gen(p, x, r)
|
||||
of nkHiddenAddr:
|
||||
gen(p, n[0], r)
|
||||
of nkConv:
|
||||
|
||||
@@ -1276,6 +1276,10 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
## The later 'injectdestructors' pass depends on it.
|
||||
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
|
||||
incl orig.flags, tfCheckedForDestructor
|
||||
# for user defined generic destructors:
|
||||
let origRoot = genericRoot(orig)
|
||||
if origRoot != nil:
|
||||
incl origRoot.flags, tfGenericHasDestructor
|
||||
|
||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||
|
||||
@@ -94,6 +94,7 @@ type
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnStdPrefix = "StdPrefix"
|
||||
warnUnknownNotes = "UnknownNotes"
|
||||
warnUser = "User",
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
# hints
|
||||
@@ -111,7 +112,6 @@ type
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -200,6 +200,7 @@ const
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnStdPrefix: "$1 needs the 'std' prefix",
|
||||
warnUnknownNotes: "$1",
|
||||
warnUser: "$1",
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
hintSuccess: "operation successful: $#",
|
||||
@@ -236,8 +237,7 @@ const
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
hintDeclaredLoc: "$1"
|
||||
]
|
||||
|
||||
const
|
||||
|
||||
@@ -221,7 +221,14 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
if i == limit: return
|
||||
inc i
|
||||
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc searchImportsAll*(c: PContext, s: PIdent, filter: TSymKinds, holding: var seq[PSym]) =
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
holding.add s
|
||||
|
||||
proc searchScopes*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
@@ -231,14 +238,12 @@ proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKi
|
||||
result.add candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
proc searchScopesAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = searchScopes(c,s,filter)
|
||||
if result.len == 0:
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
searchImportsAll(c, s, filter, result)
|
||||
|
||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
proc selectFromScopesElseAll*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
@@ -252,11 +257,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
if result.len == 0:
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
searchImportsAll(c, s, filter, result)
|
||||
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
@@ -644,7 +645,7 @@ const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
|
||||
includePureEnum = false): seq[PSym] =
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
result = selectFromScopesElseAll(c, ident, filter)
|
||||
if skEnumField in filter and (result.len == 0 or includePureEnum):
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
@@ -725,7 +726,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkOpenSym:
|
||||
# maybe the logic in semexprs should be mirrored here instead
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
|
||||
@@ -1,52 +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 generation of ``.ndi`` files for better debugging
|
||||
## support of Nim code. "ndi" stands for "Nim debug info".
|
||||
|
||||
import ast, msgs, ropes, options, pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
type
|
||||
NdiFile* = object
|
||||
enabled: bool
|
||||
f: File
|
||||
buf: string
|
||||
filename: AbsoluteFile
|
||||
syms: seq[PSym]
|
||||
|
||||
proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
|
||||
f.buf.setLen 0
|
||||
f.buf.addInt s.info.line.int
|
||||
f.buf.add "\t"
|
||||
f.buf.addInt s.info.col.int
|
||||
f.f.write(s.name.s, "\t")
|
||||
f.f.writeRope(s.loc.snippet)
|
||||
f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
|
||||
|
||||
template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
|
||||
if f.enabled: f.syms.add s
|
||||
|
||||
proc open*(f: var NdiFile; filename: AbsoluteFile; conf: ConfigRef) =
|
||||
f.enabled = not filename.isEmpty
|
||||
if f.enabled:
|
||||
f.filename = filename
|
||||
f.buf = newStringOfCap(20)
|
||||
|
||||
proc close*(f: var NdiFile, conf: ConfigRef) =
|
||||
if f.enabled:
|
||||
f.f = open(f.filename.string, fmWrite, 8000)
|
||||
doAssert f.f != nil, f.filename.string
|
||||
for s in f.syms:
|
||||
doWrite(f, s, conf)
|
||||
close(f.f)
|
||||
f.syms.reset
|
||||
f.filename.reset
|
||||
@@ -9,6 +9,8 @@ define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
define:nimPreviewCheckedClose
|
||||
define:nimPreviewAsmSemSymbol
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@@ -25,7 +25,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2024"
|
||||
copyrightYear* = "2025"
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
@@ -229,6 +229,7 @@ type
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
vtables
|
||||
typeBoundOps
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -382,6 +383,7 @@ type
|
||||
warnCounter*: int
|
||||
errorMax*: int
|
||||
maxLoopIterationsVM*: int ## VM: max iterations of all loops
|
||||
maxCallDepthVM*: int ## VM: max call depth
|
||||
isVmTrace*: bool
|
||||
configVars*: StringTableRef
|
||||
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
|
||||
@@ -597,6 +599,7 @@ proc newConfigRef*(): ConfigRef =
|
||||
arguments: "",
|
||||
suggestMaxResults: 10_000,
|
||||
maxLoopIterationsVM: 10_000_000,
|
||||
maxCallDepthVM: 2_000,
|
||||
vmProfileData: newProfileData(),
|
||||
spellSuggestMax: spellSuggestSecretSauce,
|
||||
currentConfigDir: ""
|
||||
|
||||
@@ -42,10 +42,7 @@ proc makePass*(open: TPassOpen = nil,
|
||||
process: TPassProcess = nil,
|
||||
close: TPassClose = nil,
|
||||
isFrontend = false): TPass =
|
||||
result.open = open
|
||||
result.close = close
|
||||
result.process = process
|
||||
result.isFrontend = isFrontend
|
||||
result = (open, process, close, isFrontend)
|
||||
|
||||
const
|
||||
maxPasses = 10
|
||||
@@ -100,8 +97,8 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream): bool {.discardable.} =
|
||||
if graph.stopCompile(): return true
|
||||
var
|
||||
p: Parser
|
||||
a: TPassContextArray
|
||||
p: Parser = default(Parser)
|
||||
a: TPassContextArray = default(TPassContextArray)
|
||||
s: PLLStream
|
||||
fileIdx = module.fileIdx
|
||||
prepareConfigNotes(graph, module)
|
||||
|
||||
@@ -590,7 +590,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
var customArgs = ""
|
||||
if n.kind in nkCallKinds:
|
||||
s = getStrLit(c, n, 1)
|
||||
if n.len <= 3:
|
||||
if n.len == 3:
|
||||
customArgs = getStrLit(c, n, 2)
|
||||
else:
|
||||
localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
|
||||
@@ -637,7 +637,10 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||
# XXX what to do here if 'amb' is true?
|
||||
if e != nil:
|
||||
incl(e.flags, sfUsed)
|
||||
result.add newSymNode(e)
|
||||
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
|
||||
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
|
||||
else:
|
||||
result.add newSymNode(e)
|
||||
else:
|
||||
result.add newStrNode(nkStrLit, sub)
|
||||
else:
|
||||
|
||||
@@ -500,6 +500,21 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
dec(c.config.evalTemplateCounter)
|
||||
discard c.friendModules.pop()
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
if len(n) > 1:
|
||||
result = getLineInfo(n[1])
|
||||
else:
|
||||
result = n.info
|
||||
of nkAccQuoted, nkPragmaExpr:
|
||||
if len(n) > 0:
|
||||
result = getLineInfo(n[0])
|
||||
else:
|
||||
result = n.info
|
||||
else:
|
||||
result = n.info
|
||||
|
||||
const
|
||||
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
|
||||
|
||||
@@ -526,7 +541,9 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
if efNoSemCheck notin flags:
|
||||
result = semAfterMacroCall(c, n, result, sym, flags, expectedType)
|
||||
if c.config.macrosToExpand.hasKey(sym.name.s):
|
||||
message(c.config, nOrig.info, hintExpandMacro, renderTree(result))
|
||||
message(c.config, nOrig.info, hintExpandMacro, renderTree(result, {
|
||||
renderNonExportedFields, renderDocComments, renderNoComments
|
||||
}))
|
||||
result = wrapInComesFrom(nOrig.info, sym, result)
|
||||
popInfoContext(c.config)
|
||||
|
||||
@@ -671,8 +688,9 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): P
|
||||
if child != nil:
|
||||
let node = newNode(nkIntLit)
|
||||
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
|
||||
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
|
||||
semExprWithType(c, child),
|
||||
let typeNode = newNode(nkType)
|
||||
typeNode.typ() = makeTypeDesc(c, aTypSkip[1])
|
||||
result = semExpr(c, newTree(nkCall, newTree(nkBracketExpr, newSymNode(getSysSym(c.graph, a.info, "arrayWithDefault"), a.info), typeNode),
|
||||
node
|
||||
))
|
||||
result.typ() = aTyp
|
||||
@@ -733,6 +751,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
|
||||
result.semInferredLambda = semInferredLambda
|
||||
result.semGenerateInstance = generateInstance
|
||||
result.instantiateOnlyProcType = instantiateOnlyProcType
|
||||
result.fitDefaultNode = fitDefaultNode
|
||||
result.semTypeNode = semTypeNode
|
||||
result.instTypeBoundOp = sigmatch.instTypeBoundOp
|
||||
result.hasUnresolvedArgs = hasUnresolvedArgs
|
||||
@@ -853,9 +872,9 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
|
||||
|
||||
proc reportUnusedModules(c: PContext) =
|
||||
if c.config.cmd == cmdM: return
|
||||
for i in 0..high(c.unusedImports):
|
||||
if sfUsed notin c.unusedImports[i][0].flags:
|
||||
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
|
||||
for (s, info) in c.unusedImports:
|
||||
if sfUsed notin s.flags:
|
||||
message(c.config, info, warnUnusedImportX, s.name.s)
|
||||
|
||||
proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
|
||||
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
|
||||
|
||||
@@ -56,7 +56,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
|
||||
proc name[T: static proc()]() = T()
|
||||
name[proc() = echo"hello"]()
|
||||
]#
|
||||
for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
|
||||
for paramSym in searchScopesAll(c, symx.name, {skConst}):
|
||||
let paramTyp = paramSym.typ
|
||||
if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
|
||||
result.add((paramTyp.n.sym, o.lastOverloadScope))
|
||||
@@ -69,6 +69,39 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
|
||||
result[0].scope, diagnostics)
|
||||
best.state = csNoMatch
|
||||
|
||||
proc isAttachableRoutineTo(prc: PSym, arg: PType): bool =
|
||||
result = false
|
||||
if arg.owner != prc.owner: return false
|
||||
for i in 1 ..< prc.typ.len:
|
||||
if prc.typ.n[i].kind == nkSym and prc.typ.n[i].sym.ast != nil:
|
||||
# has default value, parameter is not considered in type attachment
|
||||
continue
|
||||
let t = nominalRoot(prc.typ[i])
|
||||
if t != nil and t.itemId == arg.itemId:
|
||||
# parameter `i` is a nominal type in this module
|
||||
# attachable if the nominal root `t` has the same id as `arg`
|
||||
return true
|
||||
|
||||
proc addTypeBoundSymbols(graph: ModuleGraph, arg: PType, name: PIdent,
|
||||
filter: TSymKinds, marker: var IntSet,
|
||||
syms: var seq[tuple[s: PSym, scope: int]]) =
|
||||
# add type bound ops for `name` based on the argument type `arg`
|
||||
if arg != nil:
|
||||
# argument must be typed first, meaning arguments always
|
||||
# matching `untyped` are ignored
|
||||
let t = nominalRoot(arg)
|
||||
if t != nil and t.owner.kind == skModule:
|
||||
# search module for routines attachable to `t`
|
||||
let module = t.owner
|
||||
var iter = default(ModuleIter)
|
||||
var s = initModuleIter(iter, graph, module, name)
|
||||
while s != nil:
|
||||
if s.kind in filter and s.isAttachableRoutineTo(t) and
|
||||
not containsOrIncl(marker, s.id):
|
||||
# least priority scope, less than explicit imports:
|
||||
syms.add((s, -2))
|
||||
s = nextModuleIter(iter, graph)
|
||||
|
||||
proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
n, orig: PNode,
|
||||
initialBinding: PNode,
|
||||
@@ -88,10 +121,23 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
best, alt, o, diagnosticsFlag)
|
||||
if len(syms) == 0:
|
||||
return
|
||||
let allowTypeBoundOps = typeBoundOps in c.features and
|
||||
# qualified or bound symbols cannot refer to type bound ops
|
||||
headSymbol.kind in {nkIdent, nkAccQuoted, nkOpenSymChoice, nkOpenSym}
|
||||
var symMarker = initIntSet()
|
||||
for s in syms:
|
||||
symMarker.incl(s.s.id)
|
||||
# current overload being considered
|
||||
var sym = syms[0].s
|
||||
let name = sym.name
|
||||
var scope = syms[0].scope
|
||||
|
||||
if allowTypeBoundOps:
|
||||
for a in 1 ..< n.len:
|
||||
# for every already typed argument, add type bound ops
|
||||
let arg = n[a]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
# starts at 1 because 0 is already done with setup, only needs checking
|
||||
var nextSymIndex = 1
|
||||
var z: TCandidate # current candidate
|
||||
@@ -106,6 +152,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# may introduce new symbols with caveats described in recalc branch
|
||||
matches(c, n, orig, z)
|
||||
|
||||
if allowTypeBoundOps:
|
||||
# this match may have given some arguments new types,
|
||||
# in which case add their type bound ops as well
|
||||
# type bound ops of arguments always matching `untyped` are not considered
|
||||
for x in z.newlyTypedOperands:
|
||||
let arg = n[x]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
|
||||
if z.state == csMatch:
|
||||
# little hack so that iterators are preferred over everything else:
|
||||
if sym.kind == skIterator:
|
||||
@@ -136,7 +190,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# before any further candidate init and compare. SLOW, but rare case.
|
||||
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
|
||||
best, alt, o, diagnosticsFlag)
|
||||
|
||||
symMarker = initIntSet()
|
||||
for s in syms:
|
||||
symMarker.incl(s.s.id)
|
||||
if allowTypeBoundOps:
|
||||
for a in 1 ..< n.len:
|
||||
# for every already typed argument, add type bound ops
|
||||
let arg = n[a]
|
||||
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
|
||||
# reset counter because syms may be in a new order
|
||||
symCount = c.currentScope.symbols.counter
|
||||
nextSymIndex = 0
|
||||
@@ -912,8 +973,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
|
||||
# common case; check the only candidate has the right
|
||||
# number of generic type parameters:
|
||||
result = explicitGenericSym(c, n, s, errors, doError)
|
||||
if doError and result == nil:
|
||||
notFoundError(c, n, errors)
|
||||
if result == nil:
|
||||
if c.inGenericContext > 0:
|
||||
# same as in semOverloadedCall, make expression untyped,
|
||||
# may have failed match due to unresolved types
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
elif doError:
|
||||
notFoundError(c, n, errors)
|
||||
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
|
||||
# choose the generic proc with the proper number of type parameters.
|
||||
result = newNodeI(a.kind, getCallLineInfo(n))
|
||||
@@ -923,6 +990,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
|
||||
skFunc, skIterator}:
|
||||
let x = explicitGenericSym(c, n, candidate, errors, doError)
|
||||
if x != nil: result.add(x)
|
||||
elif c.inGenericContext > 0:
|
||||
# same as in semOverloadedCall, make expression untyped,
|
||||
# may have failed match due to unresolved types
|
||||
# any failing match stops building the symchoice for correctness,
|
||||
# can also make it untyped from the start
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
# get rid of nkClosedSymChoice if not ambiguous:
|
||||
if result.len == 0:
|
||||
result = nil
|
||||
|
||||
@@ -142,6 +142,7 @@ type
|
||||
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
|
||||
prc: PSym, info: TLineInfo): PType
|
||||
# used by sigmatch for explicit generic instantiations
|
||||
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
|
||||
includedFiles*: IntSet # used to detect recursive include files
|
||||
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
|
||||
userPragmas*: TStrTable
|
||||
|
||||
@@ -1006,9 +1006,9 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
n.typ.flags.incl tfUnresolved
|
||||
|
||||
# optimization pass: not necessary for correctness of the semantic pass
|
||||
if callee.kind == skConst or
|
||||
if (callee.kind == skConst or
|
||||
{sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||
{sfForward, sfImportc} * callee.flags == {} and n.typ != nil:
|
||||
{sfForward, sfImportc} * callee.flags == {}) and n.typ != nil:
|
||||
|
||||
if callee.kind != skConst and
|
||||
sfCompileTime notin callee.flags and
|
||||
@@ -1492,6 +1492,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess
|
||||
markUsed(c, n.info, s)
|
||||
onUse(n.info, s)
|
||||
if s.typ == nil:
|
||||
return localErrorNode(c, n, "symbol '$1' has no type" % [s.name.s])
|
||||
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
|
||||
return s.typ.n
|
||||
result = newSymNode(s, n.info)
|
||||
@@ -1678,29 +1680,46 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
|
||||
result = tryReadingGenericParam(c, n, i, t)
|
||||
flags.incl efCannotBeDotCall
|
||||
|
||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||
proc hiddenDerefDepth(n: PNode): int =
|
||||
result = 0
|
||||
var n = n
|
||||
while n.kind == nkHiddenDeref:
|
||||
inc result
|
||||
n = n[0]
|
||||
|
||||
proc dotTransformation(c: PContext, n: PNode, initialDerefs: int): PNode =
|
||||
var root = n[0]
|
||||
let currentDerefs = hiddenDerefDepth(root)
|
||||
if currentDerefs > initialDerefs:
|
||||
# hidden derefs were inserted by `builtinFieldAccess` for fields of
|
||||
# `ref object` etc.
|
||||
# undo the derefs for overload resolution
|
||||
for _ in initialDerefs ..< currentDerefs:
|
||||
root = root[0]
|
||||
root = copyTree(root)
|
||||
if isSymChoice(n[1]) or
|
||||
# generics usually leave field names as symchoices, but not types
|
||||
(n[1].kind == nkSym and n[1].sym.kind == skType):
|
||||
result = newNodeI(nkDotCall, n.info)
|
||||
result.add n[1]
|
||||
result.add copyTree(n[0])
|
||||
result.add root
|
||||
else:
|
||||
var i = considerQuotedIdent(c, n[1], n)
|
||||
result = newNodeI(nkDotCall, n.info)
|
||||
result.flags.incl nfDotField
|
||||
result.add newIdentNode(i, n[1].info)
|
||||
result.add copyTree(n[0])
|
||||
result.add root
|
||||
|
||||
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# this is difficult, because the '.' is used in many different contexts
|
||||
# in Nim. We first allow types in the semantic checking.
|
||||
var f = flags - {efIsDotCall}
|
||||
let initialDerefDepth = hiddenDerefDepth(n[0])
|
||||
result = builtinFieldAccess(c, n, f)
|
||||
if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
|
||||
efIsDotCall in flags and callOperator notin c.features and
|
||||
efCannotBeDotCall notin f):
|
||||
result = dotTransformation(c, n)
|
||||
result = dotTransformation(c, n, initialDerefDepth)
|
||||
|
||||
proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
|
||||
result = newNodeI(nkCall, n.info)
|
||||
@@ -1814,7 +1833,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = f
|
||||
# type parameters: partial generic specialization
|
||||
n[0] = semSymGenericInstantiation(c, n[0], s)
|
||||
result = maybeInstantiateGeneric(c, n, s, doError = afterOverloading)
|
||||
if result != nil:
|
||||
if result != nil and
|
||||
# leave untyped generic expression alone:
|
||||
(result.typ == nil or result.typ.kind != tyFromExpr):
|
||||
# check newly created sym/symchoice
|
||||
result = semExpr(c, result, flags)
|
||||
of skMacro, skTemplate:
|
||||
@@ -2000,13 +2021,14 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
# --> `f=` (r, x)
|
||||
let nOrig = n.copyTree
|
||||
var flags = {efLValue}
|
||||
let initialDerefDepth = hiddenDerefDepth(a[0])
|
||||
a = builtinFieldAccess(c, a, flags)
|
||||
if a == nil:
|
||||
a = propertyWriteAccess(c, n, nOrig, n[0])
|
||||
if a != nil: return a
|
||||
# we try without the '='; proc that return 'var' or macros are still
|
||||
# possible:
|
||||
a = dotTransformation(c, n[0])
|
||||
a = dotTransformation(c, n[0], initialDerefDepth)
|
||||
if a.kind == nkDotCall:
|
||||
a.transitionSonsKind(nkCall)
|
||||
a = semExprWithType(c, a, {efLValue})
|
||||
@@ -2906,7 +2928,10 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
# `const foo = if true: (0, nil) else: (1, new(int))`
|
||||
n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
|
||||
let conversion = indexTypesMatch(c, expectedElemType, n[i][1].typ, n[i][1])
|
||||
# ignore matching error, full tuple will be matched later which may call converter, see #24609
|
||||
if conversion != nil:
|
||||
n[i][1] = conversion
|
||||
|
||||
if n[i][1].typ.kind == tyTypeDesc:
|
||||
localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
|
||||
|
||||
@@ -17,9 +17,17 @@ type
|
||||
field: PSym
|
||||
replaceByFieldName: bool
|
||||
c: PContext
|
||||
leftPartOfDefinition: bool
|
||||
|
||||
proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
proc wrapNewScope(c: PContext, n: PNode): PNode {.inline.} =
|
||||
# use `if true` to not interfere with `break`
|
||||
# just opening scope via `openScope(c)` isn't enough,
|
||||
# a scope has to be opened in the codegen as well for reused
|
||||
# template instantiations
|
||||
let trueLit = newIntLit(c.graph, n.info, 1)
|
||||
trueLit.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result = newTreeI(nkIfStmt, n.info, newTreeI(nkElifBranch, n.info, trueLit, n))
|
||||
|
||||
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
if c.field != nil and isEmptyType(c.field.typ):
|
||||
result = newNode(nkEmpty)
|
||||
return
|
||||
@@ -30,9 +38,6 @@ proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
let ident = considerQuotedIdent(c.c, n)
|
||||
if c.replaceByFieldName:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
|
||||
if c.leftPartOfDefinition:
|
||||
localError(c.c.config, n.info,
|
||||
"redefine field variable '$1' in a 'fields' loop" % [ident.s])
|
||||
let fieldName = if c.tupleType.isNil: c.field.name.s
|
||||
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
|
||||
else: c.tupleType.n[c.tupleIndex].sym.name.s
|
||||
@@ -41,9 +46,6 @@ proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
# other fields:
|
||||
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
|
||||
if c.leftPartOfDefinition:
|
||||
localError(c.c.config, n.info,
|
||||
"redefine field variable '$1' in a 'fields' loop" % [ident.s])
|
||||
var call = forLoop[^2]
|
||||
var tupl = call[i+1-ord(c.replaceByFieldName)]
|
||||
if c.field.isNil:
|
||||
@@ -55,13 +57,6 @@ proc instFieldLoopBody(c: var TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
result.add(tupl)
|
||||
result.add(newSymNode(c.field, n.info))
|
||||
break
|
||||
of nkIdentDefs, nkVarTuple, nkConstDef:
|
||||
result = shallowCopy(n)
|
||||
c.leftPartOfDefinition = true
|
||||
result[0] = instFieldLoopBody(c, n[0], forLoop)
|
||||
c.leftPartOfDefinition = false
|
||||
for i in 1..<n.len:
|
||||
result[i] = instFieldLoopBody(c, n[i], forLoop)
|
||||
else:
|
||||
if n.kind == nkContinueStmt:
|
||||
localError(c.c.config, n.info,
|
||||
@@ -86,7 +81,9 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||
)
|
||||
openScope(c.c)
|
||||
inc c.c.inUnrolledContext
|
||||
let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
var body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c.c, body)
|
||||
father.add(semStmt(c.c, body, {}))
|
||||
dec c.c.inUnrolledContext
|
||||
closeScope(c.c)
|
||||
@@ -162,6 +159,8 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
replaceByFieldName: m == mFieldPairs
|
||||
)
|
||||
var body = instFieldLoopBody(fc, loopBody, n)
|
||||
# new scope for each field that codegen should know about:
|
||||
body = wrapNewScope(c, body)
|
||||
inc c.inUnrolledContext
|
||||
stmts.add(semStmt(c, body, {}))
|
||||
dec c.inUnrolledContext
|
||||
|
||||
@@ -589,7 +589,7 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
raise newException(ValueError, "invalid enum value: " & str)
|
||||
else:
|
||||
localError(g.config, s.info, "unsupported type $1 for define '$2'" %
|
||||
[name, typeToString(rawTyp)])
|
||||
[typeToString(rawTyp), name])
|
||||
except ValueError as e:
|
||||
localError(g.config, s.info,
|
||||
"could not process define '$1' of type $2; $3" %
|
||||
|
||||
@@ -218,7 +218,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
# could be type conversion if like a.T and not a.T()
|
||||
let symKinds = if inCall: routineKinds else: routineKinds+{skType}
|
||||
var candidates = searchInScopesFilterBy(c, ident, symKinds)
|
||||
var candidates = selectFromScopesElseAll(c, ident, symKinds)
|
||||
if candidates.len > 0:
|
||||
let s = candidates[0] # XXX take into account the other candidates!
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
|
||||
@@ -10,9 +10,28 @@
|
||||
## Implements type sanity checking for ASTs resulting from macros. Lots of
|
||||
## room for improvement here.
|
||||
|
||||
import ast, msgs, types, options
|
||||
import ast, msgs, types, options, trees, nimsets
|
||||
|
||||
proc ithField(n: PNode, field: var int): PSym =
|
||||
type
|
||||
FieldTracker = object
|
||||
index: int
|
||||
remaining: int
|
||||
constr: PNode
|
||||
delete: bool # to delete fields from inactive case branches
|
||||
FieldInfo = ref object
|
||||
sym: PSym
|
||||
delete: bool
|
||||
|
||||
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
|
||||
# copied from sem
|
||||
result = false
|
||||
for i in 0 ..< branch.len-1:
|
||||
if branch[i].kind == nkRange:
|
||||
if overlap(branch[i], matched): return true
|
||||
elif exprStructuralEquivalent(branch[i], matched):
|
||||
return true
|
||||
|
||||
proc ithField(n: PNode, field: var FieldTracker): FieldInfo =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
@@ -23,18 +42,42 @@ proc ithField(n: PNode, field: var int): PSym =
|
||||
if n[0].kind != nkSym: return
|
||||
result = ithField(n[0], field)
|
||||
if result != nil: return
|
||||
# value of the discriminator field, from (index - remaining - 1 + 1):
|
||||
# - 1 because the `ithField` call above decreased it by 1,
|
||||
# + 1 because the constructor node has an initial type child
|
||||
let val = field.constr[field.index - field.remaining][1]
|
||||
var branchFound = false
|
||||
for i in 1..<n.len:
|
||||
let previousDelete = field.delete
|
||||
case n[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
of nkOfBranch:
|
||||
if branchFound or previousDelete or
|
||||
not caseBranchMatchesExpr(n[i], val):
|
||||
# if this is not the active case branch,
|
||||
# mark all fields inside as deleted
|
||||
field.delete = true
|
||||
else:
|
||||
branchFound = true
|
||||
result = ithField(lastSon(n[i]), field)
|
||||
if result != nil: return
|
||||
field.delete = previousDelete
|
||||
of nkElse:
|
||||
if branchFound:
|
||||
# if this is not the active case branch,
|
||||
# mark all fields inside as deleted
|
||||
field.delete = true
|
||||
result = ithField(lastSon(n[i]), field)
|
||||
if result != nil: return
|
||||
field.delete = previousDelete
|
||||
else: discard
|
||||
of nkSym:
|
||||
if field == 0: result = n.sym
|
||||
else: dec(field)
|
||||
if field.remaining == 0:
|
||||
result = FieldInfo(sym: n.sym, delete: field.delete)
|
||||
else:
|
||||
dec(field.remaining)
|
||||
else: discard
|
||||
|
||||
proc ithField(t: PType, field: var int): PSym =
|
||||
proc ithField(t: PType, field: var FieldTracker): FieldInfo =
|
||||
var base = t.baseClass
|
||||
while base != nil:
|
||||
let b = skipTypes(base, skipPtrs)
|
||||
@@ -53,13 +96,16 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
n.typ() = t
|
||||
n[0].typ() = t
|
||||
for i in 1..<n.len:
|
||||
var j = i-1
|
||||
let field = x.ithField(j)
|
||||
var tracker = FieldTracker(index: i-1, remaining: i-1, constr: n, delete: false)
|
||||
let field = x.ithField(tracker)
|
||||
if field.isNil:
|
||||
globalError conf, n.info, "invalid field at index " & $i
|
||||
else:
|
||||
internalAssert(conf, n[i].kind == nkExprColonExpr)
|
||||
annotateType(n[i][1], field.typ, conf)
|
||||
annotateType(n[i][1], field.sym.typ, conf)
|
||||
if field.delete:
|
||||
# only codegen fields from active case branches
|
||||
incl(n[i].flags, nfPreventCg)
|
||||
of nkPar, nkTupleConstr:
|
||||
if x.kind == tyTuple:
|
||||
n.typ() = t
|
||||
|
||||
@@ -536,31 +536,33 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
else:
|
||||
if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
|
||||
setOwner(fin, fin.instantiatedFrom)
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
|
||||
selfSymNode.typ() = fin.typ.firstParamType
|
||||
wrapperSym.flags.incl sfUsed
|
||||
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
|
||||
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
|
||||
),
|
||||
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
|
||||
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
|
||||
if fin.typ[1].skipTypes(abstractInst).kind != tyRef:
|
||||
bindTypeHook(c, fin, n, attachedDestructor)
|
||||
else:
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
|
||||
selfSymNode.typ() = fin.typ.firstParamType
|
||||
wrapperSym.flags.incl sfUsed
|
||||
|
||||
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
|
||||
setOwner(transFormedSym, fin)
|
||||
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
|
||||
let origParamType = transFormedSym.ast[bodyPos][1].typ
|
||||
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
|
||||
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ() = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
# TODO: suppress var destructor warnings; if newFinalizer is not
|
||||
# TODO: deprecated, try to implement plain T destructor
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
|
||||
fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
|
||||
),
|
||||
name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
|
||||
genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
|
||||
|
||||
var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
|
||||
setOwner(transFormedSym, fin)
|
||||
if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
|
||||
let origParamType = transFormedSym.ast[bodyPos][1].typ
|
||||
let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
|
||||
let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ() = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor)
|
||||
result = addDefaultFieldForNew(c, n)
|
||||
|
||||
proc semPrivateAccess(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -79,7 +79,7 @@ proc semConstrField(c: PContext, flags: TExprFlags,
|
||||
if nfSkipFieldChecking in assignment[1].flags:
|
||||
discard
|
||||
elif not fieldVisible(c, field):
|
||||
localError(c.config, initExpr.info,
|
||||
localError(c.config, assignment[0].info,
|
||||
"the field '$1' is not accessible." % [field.name.s])
|
||||
return
|
||||
|
||||
@@ -465,11 +465,14 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
if t == nil:
|
||||
return localErrorNode(c, result, "object constructor needs an object type")
|
||||
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
when false:
|
||||
# attempted type inference for generic object types,
|
||||
# doesn't work since n[0] isn't set and seems underspecified
|
||||
if t.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
|
||||
expectedType != nil and expectedType.skipTypes({tyGenericInst,
|
||||
tyAlias, tySink, tyOwned, tyRef}).kind == tyObject:
|
||||
t = expectedType
|
||||
|
||||
t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if t.kind == tyRef:
|
||||
@@ -518,7 +521,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
for j in 1..<i:
|
||||
let prevId = considerQuotedIdent(c, result[j][0])
|
||||
if prevId.id == id.id:
|
||||
localError(c.config, field.info, errFieldInitTwice % id.s)
|
||||
localError(c.config, field[0].info, errFieldInitTwice % id.s)
|
||||
hasError = true
|
||||
break
|
||||
# 2) No such field exists in the constructed type
|
||||
|
||||
@@ -124,10 +124,11 @@ proc collectObjectTree(graph: ModuleGraph, n: PNode) =
|
||||
else:
|
||||
graph.objectTree[root].add (depthLevel, typ)
|
||||
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||
if typ == nil or sfGeneratedOp in tracked.owner.flags:
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
|
||||
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
|
||||
# don't create type bound ops for anything in a function with a `nodestroy` pragma
|
||||
# bug #21987
|
||||
# unless this is an explicit call, bug #24626
|
||||
return
|
||||
when false:
|
||||
let realType = typ.skipTypes(abstractInst)
|
||||
@@ -212,6 +213,7 @@ proc varDecl(a: PEffects; n: PNode) {.inline.} =
|
||||
proc skipHiddenDeref(n: PNode): PNode {.inline.} =
|
||||
result = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
|
||||
|
||||
proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||
let n = skipHiddenDeref(n)
|
||||
if n.kind != nkSym: return
|
||||
@@ -1071,7 +1073,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
# rebind type bounds operations after createTypeBoundOps call
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
|
||||
createTypeBoundOps(tracked, t, n.info)
|
||||
createTypeBoundOps(tracked, t, n.info, explicit = true)
|
||||
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
|
||||
if op != nil:
|
||||
n[0].sym = op
|
||||
@@ -1301,7 +1303,12 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
let last = lastSon(child)
|
||||
track(tracked, last)
|
||||
of nkCaseStmt: trackCase(tracked, n)
|
||||
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
let oldState = tracked.init.len
|
||||
track(tracked, n[0][1])
|
||||
tracked.init.setLen(oldState)
|
||||
track(tracked, n[1][0])
|
||||
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkWhileStmt:
|
||||
# 'while true' loop?
|
||||
@@ -1638,6 +1645,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
t.scopes[res.id] = t.currentBlock
|
||||
if sfNoInit in s.flags:
|
||||
# marks result "noinit"
|
||||
incl res.flags, sfNoInit
|
||||
|
||||
track(t, body)
|
||||
|
||||
@@ -1650,13 +1660,14 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
|
||||
createTypeBoundOps(t, typ, param.info)
|
||||
if isOutParam(typ) and param.id notin t.init:
|
||||
if isOutParam(typ) and param.id notin t.init and s.magic == mNone:
|
||||
message(g.config, param.info, warnProveInit, param.name.s)
|
||||
|
||||
if not isEmptyType(s.typ.returnType) and
|
||||
(s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
|
||||
strictDefs in c.features) and
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone and
|
||||
sfNoInit notin s.flags:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
if res.id notin t.init and breaksBlock(body) != bsNoReturn:
|
||||
if tfRequiresInit in s.typ.returnType.flags:
|
||||
|
||||
@@ -492,19 +492,8 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = tru
|
||||
incl(result.flags, sfGlobal)
|
||||
result.options = c.config.options
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
if len(n) > 1:
|
||||
return getLineInfo(n[1])
|
||||
of nkAccQuoted, nkPragmaExpr:
|
||||
if len(n) > 0:
|
||||
return getLineInfo(n[0])
|
||||
else:
|
||||
discard
|
||||
result = n.info
|
||||
let info = getLineInfo(n)
|
||||
if reportToNimsuggest:
|
||||
let info = getLineInfo(n)
|
||||
suggestSym(c.graph, info, result, c.graph.usageSym)
|
||||
|
||||
proc checkNilable(c: PContext; v: PSym) =
|
||||
@@ -612,11 +601,38 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
else:
|
||||
localError(c.config, n.info, "nkDotNode requires 2 children")
|
||||
|
||||
proc checkDefineType(c: PContext; v: PSym; t: PType) =
|
||||
# see semfold.foldDefine for acceptable types
|
||||
let typeKinds =
|
||||
case v.magic
|
||||
of mStrDefine: {tyString, tyCstring}
|
||||
# this used to be not typechecked, so anything that accepts int nodes for compatbility:
|
||||
of mIntDefine: {tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyChar, tyEnum}
|
||||
of mBoolDefine: {tyBool}
|
||||
of mGenericDefine: {tyString, tyCstring, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
|
||||
else: raiseAssert("unreachable")
|
||||
var skipped = abstractVarRange
|
||||
if v.magic == mGenericDefine:
|
||||
# no distinct types for generic define
|
||||
skipped.excl tyDistinct
|
||||
if t.skipTypes(skipped).kind notin typeKinds:
|
||||
let name =
|
||||
case v.magic
|
||||
of mStrDefine: "strdefine"
|
||||
of mIntDefine: "intdefine"
|
||||
of mBoolDefine: "booldefine"
|
||||
of mGenericDefine: "define"
|
||||
else: raiseAssert("unreachable")
|
||||
localError(c.config, v.info, "unsupported type for constant '" & v.name.s &
|
||||
"' with ." & name & " pragma: " & typeToString(t))
|
||||
|
||||
proc setVarType(c: PContext; v: PSym, typ: PType) =
|
||||
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
|
||||
localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
|
||||
v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
|
||||
"; new type is: " & typeToString(typ, preferDesc))
|
||||
if v.kind == skConst and v.magic in {mGenericDefine, mIntDefine, mStrDefine, mBoolDefine}:
|
||||
checkDefineType(c, v, typ)
|
||||
v.typ = typ
|
||||
|
||||
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
|
||||
@@ -1698,12 +1714,14 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
obj.ast[0] = a[0].shallowCopy
|
||||
if a[0][0].kind == nkPostfix:
|
||||
obj.ast[0][0] = a[0][0].shallowCopy
|
||||
obj.ast[0][0][0] = a[0][0][0] # ident "*"
|
||||
obj.ast[0][0][1] = symNode
|
||||
else:
|
||||
obj.ast[0][0] = symNode
|
||||
obj.ast[0][1] = a[0][1]
|
||||
of nkPostfix:
|
||||
obj.ast[0] = a[0].shallowCopy
|
||||
obj.ast[0][0] = a[0][0] # ident "*"
|
||||
obj.ast[0][1] = symNode
|
||||
else: assert(false)
|
||||
obj.ast[1] = a[1]
|
||||
@@ -1758,6 +1776,17 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
# check the style here after the pragmas have been processed:
|
||||
styleCheckDef(c, s)
|
||||
# compute the type's size and check for illegal recursions:
|
||||
if a[0].kind == nkPragmaExpr:
|
||||
let pragmas = a[0][1]
|
||||
for i in 0 ..< pragmas.len:
|
||||
if pragmas[i].kind == nkExprColonExpr and
|
||||
pragmas[i][0].kind == nkIdent and
|
||||
whichKeyword(pragmas[i][0].ident) == wSize:
|
||||
if s.typ.kind != tyEnum and sfImportc notin s.flags:
|
||||
# EventType* {.size: sizeof(uint32).} = enum
|
||||
# AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
|
||||
localError(c.config, pragmas[i].info, "size pragma only allowed for enum types and imported types")
|
||||
|
||||
if a[1].kind == nkEmpty:
|
||||
var x = a[2]
|
||||
if x.kind in nkCallKinds and nfSem in x.flags:
|
||||
@@ -2035,14 +2064,27 @@ proc canonType(c: PContext, t: PType): PType =
|
||||
else:
|
||||
result = t
|
||||
|
||||
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
|
||||
if sfOverridden notin prevOp.flags:
|
||||
proc prevDestructor(c: PContext; op: TTypeAttachedOp; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & AttachedOpToStr[op] & "' to: " & typeToString(obj)
|
||||
if prevOp == nil:
|
||||
# happens if the destructor was implicitly constructed for a specific instance,
|
||||
# not the entire generic type
|
||||
msg.add "; previous declaration was constructed implicitly"
|
||||
elif sfOverridden notin prevOp.flags:
|
||||
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
|
||||
else:
|
||||
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
|
||||
localError(c.config, info, errGenerated, msg)
|
||||
|
||||
proc checkedForDestructor(t: PType): bool =
|
||||
if tfCheckedForDestructor in t.flags:
|
||||
return true
|
||||
# maybe another instance was instantiated, marking the generic root:
|
||||
let root = genericRoot(t)
|
||||
if root != nil and tfGenericHasDestructor in root.flags:
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc whereToBindTypeHook(c: PContext; t: PType): PType =
|
||||
result = t
|
||||
while true:
|
||||
@@ -2076,10 +2118,10 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
prevDestructor(c, op, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
@@ -2092,7 +2134,7 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = case op
|
||||
@@ -2116,17 +2158,17 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressV
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and
|
||||
if op == attachedDestructor and t.firstParamType.kind == tyVar and
|
||||
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
prevDestructor(c, op, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
@@ -2217,10 +2259,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
let ao = getAttachedOp(c.graph, obj, k)
|
||||
if ao == s:
|
||||
discard "forward declared op"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
elif ao.isNil and not checkedForDestructor(obj):
|
||||
setAttachedOp(c.graph, c.module.position, obj, k, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
prevDestructor(c, k, ao, obj, n.info)
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
@@ -2309,7 +2351,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
|
||||
isInitializer = false
|
||||
break
|
||||
var j = 0
|
||||
while p[j].sym.kind == skParam:
|
||||
while p[j].kind == nkSym and p[j].sym.kind == skParam:
|
||||
initializerCall.add val
|
||||
inc j
|
||||
if isInitializer:
|
||||
@@ -2359,6 +2401,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
of nkEmpty:
|
||||
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
|
||||
s.flags.incl sfUsed
|
||||
s.flags.incl sfGenSym
|
||||
n[namePos] = newSymNode(s)
|
||||
of nkSym:
|
||||
s = n[namePos].sym
|
||||
@@ -2647,7 +2690,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
incl(s.typ.flags, tfCapturesEnv)
|
||||
else:
|
||||
s.typ.callConv = ccInline
|
||||
if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
if result[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
localError(c.config, n.info, errImplOfXexpected % s.name.s)
|
||||
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
|
||||
@@ -67,7 +67,12 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
# for instance 'nextTry' is both in tables.nim and astalgo.nim ...
|
||||
if not isField or sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
markUsed(c, info, s)
|
||||
if isField:
|
||||
# possibly not final field sym
|
||||
incl(s.flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
else:
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -691,6 +696,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
s = semIdentVis(c, skTemplate, n[namePos], {})
|
||||
assert s.kind == skTemplate
|
||||
|
||||
let info = getLineInfo(n[namePos])
|
||||
suggestSym(c.graph, info, s, c.graph.usageSym)
|
||||
|
||||
styleCheckDef(c, s)
|
||||
onDef(n[namePos].info, s)
|
||||
# check parameter list:
|
||||
|
||||
@@ -84,6 +84,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
let isPure = result.sym != nil and sfPure in result.sym.flags
|
||||
var symbols: TStrTable = initStrTable()
|
||||
var hasNull = false
|
||||
var needsReorder = false
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkEmpty: continue
|
||||
var useAutoCounter = false
|
||||
@@ -122,7 +123,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
|
||||
if i != 1:
|
||||
if x != counter: incl(result.flags, tfEnumHasHoles)
|
||||
if x != counter:
|
||||
needsReorder = true
|
||||
incl(result.flags, tfEnumHasHoles)
|
||||
e.ast = strVal # might be nil
|
||||
counter = x
|
||||
of nkSym:
|
||||
@@ -173,6 +176,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
|
||||
else:
|
||||
inc(counter)
|
||||
|
||||
if needsReorder:
|
||||
result.n.sons.sort(
|
||||
proc (x, y: PNode): int =
|
||||
result = cmp(x.sym.position, y.sym.position)
|
||||
)
|
||||
|
||||
if isPure and sfExported in result.sym.flags:
|
||||
addPureEnum(c, LazySym(sym: result.sym))
|
||||
if tfNotNil in e.typ.flags and not hasNull:
|
||||
@@ -247,27 +257,39 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
|
||||
else:
|
||||
return false
|
||||
|
||||
proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
inc c.inStaticContext
|
||||
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
|
||||
let oldType = n[^1].typ
|
||||
n[^1].flags.incl nfSem
|
||||
if n[^2].kind != nkEmpty:
|
||||
if expectedType != nil and oldType != expectedType:
|
||||
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
|
||||
changeType(c, n[^1], expectedType, true) # infer types for default fields value
|
||||
# bug #22926; be cautious that it uses `semConstExpr` to
|
||||
# evaulate the default fields; it's only natural to use
|
||||
# `changeType` to infer types for constant values
|
||||
# that's also the reason why we don't use `semExpr` to check
|
||||
# the type since two overlapping error messages might be produced
|
||||
result = n[^1].typ
|
||||
proc annotateClosureConv(n: PNode) =
|
||||
case n.kind
|
||||
of {nkNone..nkNilLit}:
|
||||
discard
|
||||
of nkTupleConstr:
|
||||
if n.typ.kind == tyProc and n.typ.callConv == ccClosure and
|
||||
n[0].typ.kind == tyProc and n[0].typ.callConv != ccClosure:
|
||||
# restores `transf.generateThunk`
|
||||
n[0] = newTreeIT(nkHiddenSubConv, n[0].info, n.typ,
|
||||
newNodeI(nkEmpty, n[0].info), n[0])
|
||||
n.transitionSonsKind(nkClosure)
|
||||
n.flags.incl nfTransf
|
||||
else:
|
||||
result = n[^1].typ
|
||||
for i in 0..<n.len:
|
||||
annotateClosureConv(n[i])
|
||||
|
||||
proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
|
||||
inc c.inStaticContext
|
||||
n = semConstExpr(c, n, expectedType = expectedType)
|
||||
let oldType = n.typ
|
||||
n.flags.incl nfSem
|
||||
if expectedType != nil and oldType != expectedType:
|
||||
n = fitNodeConsiderViewType(c, expectedType, n, n.info)
|
||||
changeType(c, n, expectedType, true) # infer types for default fields value
|
||||
# bug #22926; be cautious that it uses `semConstExpr` to
|
||||
# evaulate the default fields; it's only natural to use
|
||||
# `changeType` to infer types for constant values
|
||||
# that's also the reason why we don't use `semExpr` to check
|
||||
# the type since two overlapping error messages might be produced
|
||||
annotateClosureConv(n)
|
||||
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
|
||||
if n[^1].kind != nkNilLit:
|
||||
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||
if n.kind != nkNilLit:
|
||||
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||
dec c.inStaticContext
|
||||
|
||||
proc isRecursiveType*(t: PType): bool =
|
||||
@@ -387,8 +409,12 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
result = makeRangeWithStaticExpr(c, e.typ.n)
|
||||
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
||||
if e.intVal < 0:
|
||||
localError(c.config, n.info,
|
||||
"Array length can't be negative, but was " & $e.intVal)
|
||||
if e.kind in {nkIntLit..nkInt64Lit}:
|
||||
localError(c.config, n.info,
|
||||
"Array length can't be negative, but was " & $e.intVal)
|
||||
else:
|
||||
localError(c.config, n.info,
|
||||
"Array length can't exceed its maximum value (9223372036854775807), but was " & $cast[BiggestUInt](e.intVal))
|
||||
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||
elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc):
|
||||
if e.typ.kind == tyStatic:
|
||||
@@ -494,7 +520,14 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
var hasDefaultField = a[^1].kind != nkEmpty
|
||||
if hasDefaultField:
|
||||
typ = fitDefaultNode(c, a)
|
||||
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
|
||||
if c.inGenericContext > 0:
|
||||
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
|
||||
if typ == nil:
|
||||
typ = a[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, a[^1], typ)
|
||||
typ = a[^1].typ
|
||||
elif a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||
@@ -858,8 +891,15 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
var typ: PType
|
||||
var hasDefaultField = n[^1].kind != nkEmpty
|
||||
if hasDefaultField:
|
||||
typ = fitDefaultNode(c, n)
|
||||
propagateToOwner(rectype, typ)
|
||||
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
if c.inGenericContext > 0:
|
||||
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
|
||||
if typ == nil:
|
||||
typ = n[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, n[^1], typ)
|
||||
typ = n[^1].typ
|
||||
propagateToOwner(rectype, typ)
|
||||
elif n[^2].kind == nkEmpty:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
typ = errorType(c)
|
||||
@@ -1631,6 +1671,11 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
var err = "cannot instantiate "
|
||||
err.addTypeHeader(c.config, t)
|
||||
err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)]
|
||||
if m.firstMismatch.kind == kTypeMismatch and m.firstMismatch.arg < n.len:
|
||||
let nArg = n[m.firstMismatch.arg]
|
||||
if nArg.kind in nkSymChoices:
|
||||
err.add "\n"
|
||||
err.add ambiguousIdentifierMsg(nArg)
|
||||
localError(c.config, n.info, errGenerated, err)
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
|
||||
@@ -1831,7 +1876,7 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
|
||||
x.add(operand.copyTreeWithoutNode(p))
|
||||
# recursion assures that this works for multiple macro annotations too:
|
||||
var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared})
|
||||
if r != nil:
|
||||
if r != nil and (r.typ == nil or r.typ.kind != tyFromExpr):
|
||||
doAssert r[0].kind == nkSym
|
||||
let m = r[0].sym
|
||||
case m.kind
|
||||
|
||||
@@ -80,7 +80,7 @@ type
|
||||
owner*: PSym # where this instantiation comes from
|
||||
recursionLimit: int
|
||||
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType
|
||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
|
||||
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
|
||||
|
||||
@@ -95,8 +95,8 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
|
||||
debug t
|
||||
writeStackTrace()
|
||||
|
||||
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||
result = replaceTypeVarsTAux(cl, t)
|
||||
proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
|
||||
result = replaceTypeVarsTAux(cl, t, isInstValue)
|
||||
checkMetaInvariants(cl, result)
|
||||
|
||||
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
@@ -276,6 +276,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
|
||||
replaceTypeVarsS(cl, n.sym, result.typ)
|
||||
else:
|
||||
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
|
||||
if result.sym.kind == skField and result.sym.ast != nil and
|
||||
(cl.owner == nil or result.sym.owner == cl.owner):
|
||||
# instantiate default value of object/tuple field
|
||||
cl.c.fitDefaultNode(cl.c, result.sym.ast, result.sym.typ)
|
||||
result.sym.typ = result.sym.ast.typ
|
||||
# sym type can be nil if was gensym created by macro, see #24048
|
||||
if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
|
||||
# don't add the 'void' field
|
||||
@@ -339,7 +344,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
|
||||
#[
|
||||
|
||||
We cannot naively check for symbol recursions, because otherwise
|
||||
object types A, B whould share their fields!
|
||||
object types A, B would share their fields!
|
||||
|
||||
import tables
|
||||
|
||||
@@ -476,7 +481,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
return
|
||||
|
||||
let bbody = last body
|
||||
var newbody = replaceTypeVarsT(cl, bbody)
|
||||
var newbody = replaceTypeVarsT(cl, bbody, isInstValue = true)
|
||||
cl.skipTypedesc = oldSkipTypedesc
|
||||
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
|
||||
result.flags = result.flags + newbody.flags - tfInstClearedFlags
|
||||
@@ -573,7 +578,7 @@ proc propagateFieldFlags(t: PType, n: PNode) =
|
||||
propagateFieldFlags(t, son)
|
||||
else: discard
|
||||
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType =
|
||||
template bailout =
|
||||
if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
|
||||
# In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see
|
||||
@@ -707,13 +712,17 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
propagateToOwner(result, result.last)
|
||||
|
||||
else:
|
||||
if containsGenericType(t):
|
||||
if containsGenericType(t) or
|
||||
# nominal types as direct generic instantiation values
|
||||
# are re-instantiated even if they don't contain generic fields
|
||||
(isInstValue and (t.kind in {tyDistinct, tyObject} or isRefPtrObject(t))):
|
||||
#if not cl.allowMetaTypes:
|
||||
bailout()
|
||||
result = instCopyType(cl, t)
|
||||
result.size = -1 # needs to be recomputed
|
||||
#if not cl.allowMetaTypes:
|
||||
cl.localCache[t.itemId] = result
|
||||
let propagateInstValue = isInstValue and isRefPtrObject(t)
|
||||
|
||||
for i, resulti in result.ikids:
|
||||
if resulti != nil:
|
||||
@@ -723,7 +732,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
typeToString(result[i], preferDesc) &
|
||||
"' inside of type definition: '" &
|
||||
t.owner.name.s & "'; Maybe generic arguments are missing?")
|
||||
var r = replaceTypeVarsT(cl, resulti)
|
||||
var r = replaceTypeVarsT(cl, resulti, isInstValue = propagateInstValue)
|
||||
if result.kind == tyObject:
|
||||
# carefully coded to not skip the precious tyGenericInst:
|
||||
let r2 = r.skipTypes({tyAlias, tySink, tyOwned})
|
||||
|
||||
@@ -85,6 +85,9 @@ type
|
||||
inheritancePenalty: int
|
||||
firstMismatch*: MismatchInfo # mismatch info for better error messages
|
||||
diagnosticsEnabled*: bool
|
||||
newlyTypedOperands*: seq[int]
|
||||
## indexes of arguments that are newly typechecked in this match
|
||||
## used for type bound op additions
|
||||
|
||||
TTypeRelFlag* = enum
|
||||
trDontBind
|
||||
@@ -592,45 +595,6 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
else: result = isIntConv
|
||||
else: result = isNone
|
||||
|
||||
proc reduceToBase(f: PType): PType =
|
||||
#[
|
||||
Returns the lowest order (most general) type that that is compatible with the input.
|
||||
E.g.
|
||||
A[T] = ptr object ... A -> ptr object
|
||||
A[N: static[int]] = array[N, int] ... A -> array
|
||||
]#
|
||||
case f.kind:
|
||||
of tyGenericParam:
|
||||
if f.len <= 0 or f.skipModifier == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericInvocation:
|
||||
result = reduceToBase(f.baseClass)
|
||||
of tyCompositeTypeClass, tyAlias:
|
||||
if not f.hasElementType or f.elementType == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.elementType)
|
||||
of tyGenericInst:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericBody:
|
||||
result = reduceToBase(f.typeBodyImpl)
|
||||
of tyUserTypeClass:
|
||||
if f.isResolvedUserTypeClass:
|
||||
result = f.base # ?? idk if this is right
|
||||
else:
|
||||
result = f.skipModifier
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = reduceToBase(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = reduceToBase(f.base)
|
||||
of tyRange:
|
||||
result = f.elementType
|
||||
else:
|
||||
result = f
|
||||
|
||||
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if fGenericOrigin != nil and last.kind == tyGenericInst and
|
||||
last.kidsLen-1 == fGenericOrigin.kidsLen:
|
||||
@@ -639,12 +603,22 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if x == nil:
|
||||
put(c, fGenericOrigin[i], last[i])
|
||||
|
||||
proc isGenericObjectOf(f, a: PType): bool =
|
||||
## checks if `f` is an unparametrized generic type
|
||||
## that `a` is an instance of
|
||||
if not (f.sym != nil and f.sym.typ.kind == tyGenericBody):
|
||||
# covers the case where `f` is the last child (body) of the `tyGenericBody`
|
||||
return false
|
||||
let aRoot = genericRoot(a)
|
||||
# use sym equality to check if the `tyGenericBody` types are equal
|
||||
result = aRoot != nil and f.sym == aRoot.sym
|
||||
|
||||
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||
var t = a
|
||||
assert t.kind == tyObject
|
||||
var depth = 0
|
||||
var last = a
|
||||
while t != nil and not sameObjectTypes(f, t):
|
||||
while t != nil and not (sameObjectTypes(f, t) or isGenericObjectOf(f, t)):
|
||||
if t.kind != tyObject: # avoid entering generic params etc
|
||||
return -1
|
||||
t = t.baseClass
|
||||
@@ -1865,9 +1839,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let target = f.genericHead
|
||||
let targetKind = target.kind
|
||||
var effectiveArgType = reduceToBase(a)
|
||||
# the skipped child of tyBuiltInTypeClass can be structured differently,
|
||||
# newConstraint constructs them with no children
|
||||
let typeClassArg = effectiveArgType.kind == tyBuiltInTypeClass
|
||||
effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
|
||||
if targetKind == effectiveArgType.kind:
|
||||
if effectiveArgType.isEmptyContainer:
|
||||
if not typeClassArg and effectiveArgType.isEmptyContainer:
|
||||
return isNone
|
||||
if targetKind == tyProc:
|
||||
if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
|
||||
@@ -2728,7 +2705,7 @@ proc setSon(father: PNode, at: int, son: PNode) =
|
||||
# father[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid))
|
||||
|
||||
# we are allowed to modify the calling node in the 'prepare*' procs:
|
||||
proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
|
||||
proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool): PNode =
|
||||
if formal.kind == tyUntyped and formal.len != 1:
|
||||
# {tyTypeDesc, tyUntyped, tyTyped, tyError}:
|
||||
# a.typ == nil is valid
|
||||
@@ -2746,15 +2723,17 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
|
||||
#elif formal.kind == tyTyped: {efDetermineType, efWantStmt}
|
||||
#else: {efDetermineType}
|
||||
result = c.semOperand(c, a, flags)
|
||||
newlyTyped = true
|
||||
else:
|
||||
result = a
|
||||
considerGenSyms(c, result)
|
||||
if result.kind != nkHiddenDeref and result.typ.kind in {tyVar, tyLent} and c.matchedConcept == nil:
|
||||
result = newDeref(result)
|
||||
|
||||
proc prepareOperand(c: PContext; a: PNode): PNode =
|
||||
proc prepareOperand(c: PContext; a: PNode, newlyTyped: var bool): PNode =
|
||||
if a.typ.isNil:
|
||||
result = c.semOperand(c, a, {efDetermineType})
|
||||
newlyTyped = true
|
||||
else:
|
||||
result = a
|
||||
considerGenSyms(c, result)
|
||||
@@ -2880,7 +2859,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
noMatch()
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
n[a][1] = prepareOperand(c, formal.typ, n[a][1])
|
||||
var newlyTyped = false
|
||||
n[a][1] = prepareOperand(c, formal.typ, n[a][1], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
n[a].typ() = n[a][1].typ
|
||||
arg = paramTypesMatch(m, formal.typ, n[a].typ,
|
||||
n[a][1], n[a][1])
|
||||
@@ -2904,7 +2885,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
if tfVarargs in m.callee.flags:
|
||||
# is ok... but don't increment any counters...
|
||||
# we have no formal here to snoop at:
|
||||
n[a] = prepareOperand(c, n[a])
|
||||
var newlyTyped = false
|
||||
n[a] = prepareOperand(c, n[a], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
if skipTypes(n[a].typ, abstractVar-{tyTypeDesc}).kind==tyString:
|
||||
m.call.add implicitConv(nkHiddenStdConv,
|
||||
getSysType(c.graph, n[a].info, tyCstring),
|
||||
@@ -2918,7 +2901,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
incl(marker, formal.position)
|
||||
n[a] = prepareOperand(c, formal.typ, n[a])
|
||||
var newlyTyped = false
|
||||
n[a] = prepareOperand(c, formal.typ, n[a], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
arg = paramTypesMatch(m, formal.typ, n[a].typ,
|
||||
n[a], nOrig[a])
|
||||
if arg != nil and m.baseTypeMatch and container != nil:
|
||||
@@ -2954,7 +2939,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
else:
|
||||
m.baseTypeMatch = false
|
||||
m.typedescMatched = false
|
||||
n[a] = prepareOperand(c, formal.typ, n[a])
|
||||
var newlyTyped = false
|
||||
n[a] = prepareOperand(c, formal.typ, n[a], newlyTyped)
|
||||
if newlyTyped: m.newlyTypedOperands.add(a)
|
||||
arg = paramTypesMatch(m, formal.typ, n[a].typ,
|
||||
n[a], nOrig[a])
|
||||
if arg == nil:
|
||||
|
||||
@@ -106,6 +106,13 @@ proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc transformSonsAfterType(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = newTransNode(n)
|
||||
assert n.len != 0
|
||||
result[0] = copyTree(n[0])
|
||||
for i in 1..<n.len:
|
||||
result[i] = transform(c, n[i], noConstFold)
|
||||
|
||||
proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
|
||||
result = newTransNode(kind, ri.info, 2)
|
||||
result[0] = le
|
||||
@@ -366,6 +373,19 @@ proc transformAsgn(c: PTransf, n: PNode): PNode =
|
||||
result[0] = letSection
|
||||
result[1] = asgnNode
|
||||
|
||||
template assignTupleUnpacking(c: PTransf, e: PNode) =
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
if c.transCon.forStmt[i].kind == nkVarTuple:
|
||||
for j in 0..<c.transCon.forStmt[i].len-1:
|
||||
let lhs = c.transCon.forStmt[i][j]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, e, i), j))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
else:
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
|
||||
|
||||
proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
proc asgnTo(lhs: PNode, rhs: PNode): PNode =
|
||||
# Choose the right assignment instruction according to the given ``lhs``
|
||||
@@ -400,7 +420,8 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, v)
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
elif e.kind notin {nkAddr, nkHiddenAddr}: # no need to generate temp for address operation
|
||||
elif e.kind notin {nkAddr, nkHiddenAddr} and e.kind != nkSym:
|
||||
# no need to generate temp for address operation + nodes without sideeffects
|
||||
# TODO do not use temp for nodes which cannot have side-effects
|
||||
var tmp = newTemp(c, e.typ, e.info)
|
||||
let v = newNodeI(nkVarSection, e.info)
|
||||
@@ -408,21 +429,9 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
|
||||
result.add transform(c, v)
|
||||
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
if c.transCon.forStmt[i].kind == nkVarTuple:
|
||||
for j in 0..<c.transCon.forStmt[i].len-1:
|
||||
let lhs = c.transCon.forStmt[i][j]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
else:
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
assignTupleUnpacking(c, tmp)
|
||||
else:
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
let lhs = c.transCon.forStmt[i]
|
||||
let rhs = transform(c, newTupleAccess(c.graph, e, i))
|
||||
result.add(asgnTo(lhs, rhs))
|
||||
assignTupleUnpacking(c, e)
|
||||
else:
|
||||
if c.transCon.forStmt[0].kind == nkVarTuple:
|
||||
var notLiteralTuple = false # we don't generate temp for tuples with const value: (1, 2, 3)
|
||||
@@ -435,7 +444,8 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
else:
|
||||
notLiteralTuple = true
|
||||
|
||||
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple:
|
||||
if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple and e.kind != nkSym:
|
||||
# no need to generate temp for address operation + nodes without sideeffects
|
||||
# TODO do not use temp for nodes which cannot have side-effects
|
||||
var tmp = newTemp(c, e.typ, e.info)
|
||||
let v = newNodeI(nkVarSection, e.info)
|
||||
@@ -507,7 +517,9 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false)
|
||||
) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
|
||||
n.typ.kind == tyVar and
|
||||
n.typ.skipTypes(abstractVar).kind == tyOpenArray and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyString)
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyString) and
|
||||
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef and
|
||||
n[0][0].kind == nkObjConstr)
|
||||
: # elimination is harmful to `for tuple unpack` because of newTupleAccess
|
||||
# it is also harmful to openArrayLoc (var openArray) for strings
|
||||
# addr ( deref ( x )) --> x
|
||||
@@ -637,6 +649,12 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
||||
case arg.kind
|
||||
of nkStmtListExpr:
|
||||
return paComplexOpenarray
|
||||
of nkCall:
|
||||
if skipTypes(arg.typ, abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
# XXX incorrect, causes #13417 when `arg` has side effects.
|
||||
return paDirectMapping
|
||||
else:
|
||||
return paComplexOpenarray
|
||||
of nkBracket:
|
||||
return paFastAsgnTakeTypeFromArg
|
||||
else:
|
||||
@@ -803,7 +821,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true))
|
||||
newC.mapping[formal.itemId] = newDeref(temp)
|
||||
of paComplexOpenarray:
|
||||
# arrays will deep copy here (pretty bad).
|
||||
# XXX arrays will deep copy here (pretty bad).
|
||||
var temp = newTemp(c, arg.typ, formal.info)
|
||||
addVar(v, temp)
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
|
||||
@@ -857,9 +875,18 @@ proc transformArrayAccess(c: PTransf, n: PNode): PNode =
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skType:
|
||||
result = n
|
||||
else:
|
||||
result = newTransNode(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, skipConv(n[i]))
|
||||
result = transformSons(c, n)
|
||||
if n.len >= 2 and result[1].kind in {nkChckRange, nkChckRange64} and
|
||||
n[1].kind in {nkHiddenStdConv, nkHiddenSubConv}:
|
||||
# implicit conversion, was transformed into range check
|
||||
# remove in favor of index check if conversion to array index type
|
||||
# has to be done here because the array index type needs to be relaxed
|
||||
# i.e. a uint32 index can implicitly convert to range[0..3] but not int
|
||||
let arr = skipTypes(n[0].typ, abstractVarRange)
|
||||
if arr.kind == tyArray and
|
||||
firstOrd(c.graph.config, arr) == getOrdValue(result[1][1]) and
|
||||
lastOrd(c.graph.config, arr) == getOrdValue(result[1][2]):
|
||||
result[1] = result[1].skipConv
|
||||
|
||||
proc getMergeOp(n: PNode): PSym =
|
||||
case n.kind
|
||||
@@ -1069,9 +1096,7 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkHiddenDeref}, isAddr = true)
|
||||
of nkAddr:
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
|
||||
of nkDerefExpr:
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
@@ -1084,6 +1109,9 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = transformConv(c, n)
|
||||
of nkObjConstr, nkCast:
|
||||
# don't try to transform type node
|
||||
result = transformSonsAfterType(c, n)
|
||||
of nkDiscardStmt:
|
||||
result = n
|
||||
if n[0].kind != nkEmpty:
|
||||
|
||||
@@ -209,9 +209,6 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
|
||||
else:
|
||||
result = t
|
||||
of tyConcept:
|
||||
if kind != skParam: result = t
|
||||
else: result = nil
|
||||
|
||||
proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
|
||||
# returns 'nil' on success and otherwise the part of the type that is
|
||||
|
||||
@@ -102,6 +102,9 @@ const
|
||||
# typedescX is used if we're sure tyTypeDesc should be included (or skipped)
|
||||
typedescPtrs* = abstractPtrs + {tyTypeDesc}
|
||||
typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
|
||||
|
||||
# incorrect definition of `[]` and `[]=` for these types in system.nim
|
||||
arrPutGetMagicApplies* = {tyArray, tyOpenArray, tyString, tySequence, tyCstring, tyTuple}
|
||||
|
||||
proc invalidGenericInst*(f: PType): bool =
|
||||
result = f.kind == tyGenericInst and skipModifier(f) == nil
|
||||
@@ -1201,17 +1204,19 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
|
||||
if not result: return
|
||||
|
||||
proc isGenericAlias*(t: PType): bool =
|
||||
return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst
|
||||
return t.kind == tyGenericInst and t.skipModifier.skipTypes({tyAlias}).kind == tyGenericInst
|
||||
|
||||
proc genericAliasDepth*(t: PType): int =
|
||||
result = 0
|
||||
var it = t
|
||||
var it = t.skipTypes({tyAlias})
|
||||
while it.isGenericAlias:
|
||||
it = it.skipModifier
|
||||
it = it.skipModifier.skipTypes({tyAlias})
|
||||
inc result
|
||||
|
||||
proc skipGenericAlias*(t: PType): PType =
|
||||
return if t.isGenericAlias: t.skipModifier else: t
|
||||
result = t.skipTypes({tyAlias})
|
||||
if result.isGenericAlias:
|
||||
result = result.skipModifier.skipTypes({tyAlias})
|
||||
|
||||
proc sameFlags*(a, b: PType): bool {.inline.} =
|
||||
result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
|
||||
@@ -1304,16 +1309,34 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
|
||||
of tyObject:
|
||||
withoutShallowFlags:
|
||||
result = sameFlags(a, b)
|
||||
if result:
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
cycleCheck()
|
||||
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
|
||||
# should be generic, and belong to the same generic head type:
|
||||
assert a.typeInst != nil, "generic object " & $a & " has no typeInst"
|
||||
assert b.typeInst != nil, "generic object " & $b & " has no typeInst"
|
||||
if result:
|
||||
withoutShallowFlags:
|
||||
# this is required because of generic `ref object`s,
|
||||
# the value of their dereferences are not wrapped in `tyGenericInst`,
|
||||
# so we need to check the generic parameters here
|
||||
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
of tyDistinct:
|
||||
cycleCheck()
|
||||
if c.cmp == dcEq:
|
||||
if sameFlags(a, b):
|
||||
result = sameFlags(a, b)
|
||||
if result:
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
result = sameTypeAux(a.elementType, b.elementType, c)
|
||||
# should be generic, and belong to the same generic head type:
|
||||
assert a.typeInst != nil, "generic distinct type " & $a & " has no typeInst"
|
||||
assert b.typeInst != nil, "generic distinct type " & $b & " has no typeInst"
|
||||
withoutShallowFlags:
|
||||
# just in case `tyGenericInst` was skipped at some point,
|
||||
# we need to check the generic parameters here
|
||||
for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
else:
|
||||
result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
|
||||
of tyEnum, tyForward:
|
||||
@@ -1935,3 +1958,125 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
|
||||
result = false
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc isRefPtrObject*(t: PType): bool =
|
||||
t.kind in {tyRef, tyPtr} and tfRefsAnonObj in t.flags
|
||||
|
||||
proc nominalRoot*(t: PType): PType =
|
||||
## the "name" type of a given instance of a nominal type,
|
||||
## i.e. the type directly associated with the symbol where the root
|
||||
## nominal type of `t` was defined, skipping things like generic instances,
|
||||
## aliases, `var`/`sink`/`typedesc` modifiers
|
||||
##
|
||||
## instead of returning the uninstantiated body of a generic type,
|
||||
## returns the type of the symbol instead (with tyGenericBody type)
|
||||
result = nil
|
||||
case t.kind
|
||||
of tyAlias, tyVar, tySink:
|
||||
# varargs?
|
||||
result = nominalRoot(t.skipModifier)
|
||||
of tyTypeDesc:
|
||||
# for proc foo(_: type T)
|
||||
result = nominalRoot(t.skipModifier)
|
||||
of tyGenericInvocation, tyGenericInst:
|
||||
result = t
|
||||
# skip aliases, so this works in the same module but not in another module:
|
||||
# type Foo[T] = object
|
||||
# type Bar[T] = Foo[T]
|
||||
# proc foo[T](x: Bar[T]) = ... # attached to type
|
||||
while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
result = result.skipModifier
|
||||
result = nominalRoot(result[0])
|
||||
of tyGenericBody:
|
||||
result = t
|
||||
# this time skip the aliases but take the generic body
|
||||
while result.skipModifier.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
result = result.skipModifier[0]
|
||||
let val = result.skipModifier
|
||||
if val.kind in {tyDistinct, tyEnum, tyObject} or
|
||||
isRefPtrObject(val):
|
||||
# atomic nominal types, this generic body is attached to them
|
||||
discard
|
||||
else:
|
||||
result = nominalRoot(val)
|
||||
of tyCompositeTypeClass:
|
||||
# parameter with type Foo
|
||||
result = nominalRoot(t.skipModifier)
|
||||
of tyGenericParam:
|
||||
if t.genericParamHasConstraints:
|
||||
# T: Foo
|
||||
result = nominalRoot(t.genericConstraint)
|
||||
else:
|
||||
result = nil
|
||||
of tyDistinct, tyEnum, tyObject:
|
||||
result = t
|
||||
of tyPtr, tyRef:
|
||||
if tfRefsAnonObj in t.flags:
|
||||
# in the case that we have `type Foo = ref object` etc
|
||||
result = t
|
||||
else:
|
||||
# we could allow this in general, but there's things like `seq[Foo]`
|
||||
#result = nominalRoot(t.skipModifier)
|
||||
result = nil
|
||||
of tyStatic:
|
||||
result = nominalRoot(t.base)
|
||||
else:
|
||||
# skips all typeclasses
|
||||
# is this correct for `concept`?
|
||||
result = nil
|
||||
|
||||
proc genericRoot*(t: PType): PType =
|
||||
## gets the root generic type (`tyGenericBody`) from `t`,
|
||||
## if `t` is a generic type or the body of a generic instantiation
|
||||
case t.kind
|
||||
of tyGenericBody:
|
||||
result = t
|
||||
of tyGenericInst, tyGenericInvocation:
|
||||
result = t.genericHead
|
||||
else:
|
||||
if t.typeInst != nil:
|
||||
result = t.typeInst.genericHead
|
||||
elif t.sym != nil and t.sym.typ.kind == tyGenericBody:
|
||||
# can happen if `t` is the last child (body) of the generic body
|
||||
result = t.sym.typ
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc reduceToBase*(f: PType): PType =
|
||||
#[
|
||||
Returns the lowest order (most general) type that that is compatible with the input.
|
||||
E.g.
|
||||
A[T] = ptr object ... A -> ptr object
|
||||
A[N: static[int]] = array[N, int] ... A -> array
|
||||
]#
|
||||
case f.kind:
|
||||
of tyGenericParam:
|
||||
if f.len <= 0 or f.skipModifier == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericInvocation:
|
||||
result = reduceToBase(f.baseClass)
|
||||
of tyCompositeTypeClass, tyAlias:
|
||||
if not f.hasElementType or f.elementType == nil:
|
||||
result = f
|
||||
else:
|
||||
result = reduceToBase(f.elementType)
|
||||
of tyGenericInst:
|
||||
result = reduceToBase(f.skipModifier)
|
||||
of tyGenericBody:
|
||||
result = reduceToBase(f.typeBodyImpl)
|
||||
of tyUserTypeClass:
|
||||
if f.isResolvedUserTypeClass:
|
||||
result = f.base # ?? idk if this is right
|
||||
else:
|
||||
result = f.skipModifier
|
||||
of tyStatic, tyOwned, tyVar, tyLent, tySink:
|
||||
result = reduceToBase(f.base)
|
||||
of tyInferred:
|
||||
# This is not true "After a candidate type is selected"
|
||||
result = reduceToBase(f.base)
|
||||
of tyRange:
|
||||
result = f.elementType
|
||||
else:
|
||||
result = f
|
||||
|
||||
@@ -516,6 +516,8 @@ const
|
||||
errIllegalConvFromXtoY = "illegal conversion from '$1' to '$2'"
|
||||
errTooManyIterations = "interpretation requires too many iterations; " &
|
||||
"if you are sure this is not a bug in your code, compile with `--maxLoopIterationsVM:number` (current value: $1)"
|
||||
errCallDepthExceeded = "maximum call depth for the VM exceeded; " &
|
||||
"if you are sure this is not a bug in your code, compile with `--maxCallDepthVM:number` (current value: $1)"
|
||||
errFieldXNotFound = "node lacks field: "
|
||||
|
||||
|
||||
@@ -590,6 +592,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let newPc = c.cleanUpOnReturn(tos)
|
||||
# Perform any cleanup action before returning
|
||||
if newPc < 0:
|
||||
inc(c.callDepth)
|
||||
pc = tos.comesFrom
|
||||
let retVal = regs[0]
|
||||
tos = tos.next
|
||||
@@ -1445,6 +1448,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
newFrame.slots[i] = regs[rb+i]
|
||||
if isClosure:
|
||||
newFrame.slots[rc] = TFullReg(kind: rkNode, node: regs[rb].node[1])
|
||||
if c.callDepth <= 0:
|
||||
if allowInfiniteRecursion in c.features:
|
||||
c.callDepth = c.config.maxCallDepthVM
|
||||
else:
|
||||
msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep})
|
||||
stackTraceAux(c, tos, pc)
|
||||
globalError(c.config, c.debug[pc], errCallDepthExceeded % $c.config.maxCallDepthVM)
|
||||
dec(c.callDepth)
|
||||
tos = newFrame
|
||||
updateRegsAlias
|
||||
# -1 for the following 'inc pc'
|
||||
@@ -2311,6 +2322,7 @@ proc execute(c: PCtx, start: int): PNode =
|
||||
|
||||
proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
c.loopIterations = c.config.maxLoopIterationsVM
|
||||
c.callDepth = c.config.maxCallDepthVM
|
||||
if sym.kind in routineKinds:
|
||||
if sym.typ.paramsLen != args.len:
|
||||
result = nil
|
||||
|
||||
@@ -201,6 +201,7 @@ type
|
||||
TSandboxFlag* = enum ## what the evaluation engine should allow
|
||||
allowCast, ## allow unsafe language feature: 'cast'
|
||||
allowInfiniteLoops ## allow endless loops
|
||||
allowInfiniteRecursion ## allow infinite recursion
|
||||
TSandboxFlags* = set[TSandboxFlag]
|
||||
|
||||
TSlotKind* = enum # We try to re-use slots in a smart way to
|
||||
@@ -257,7 +258,7 @@ type
|
||||
mode*: TEvalMode
|
||||
features*: TSandboxFlags
|
||||
traceActive*: bool
|
||||
loopIterations*: int
|
||||
loopIterations*, callDepth*: int
|
||||
comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
|
||||
callbacks*: seq[VmCallback]
|
||||
callbackIndex*: Table[string, int]
|
||||
@@ -294,6 +295,7 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator
|
||||
PCtx(code: @[], debug: @[],
|
||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||
prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
|
||||
callDepth: g.config.maxCallDepthVM,
|
||||
comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "",
|
||||
cache: cache, config: g.config, graph: g, idgen: idgen)
|
||||
|
||||
@@ -301,6 +303,7 @@ proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
|
||||
c.module = module
|
||||
c.prc = PProc(blocks: @[])
|
||||
c.loopIterations = c.config.maxLoopIterationsVM
|
||||
c.callDepth = c.config.maxCallDepthVM
|
||||
c.idgen = idgen
|
||||
|
||||
proc reverseName(s: string): string =
|
||||
|
||||
@@ -1543,7 +1543,9 @@ proc setSlot(c: PCtx; v: PSym) =
|
||||
v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
|
||||
|
||||
template cannotEval(c: PCtx; n: PNode) =
|
||||
if c.config.cmd == cmdCheck:
|
||||
if c.config.cmd == cmdCheck and c.config.m.errorOutputs != {}:
|
||||
# nim check command with no error outputs doesn't need to cascade here,
|
||||
# includes `tryConstExpr` case which should not continue generating code
|
||||
localError(c.config, n.info, "cannot evaluate at compile time: " &
|
||||
n.renderTree)
|
||||
c.cannotEval = true
|
||||
@@ -2038,7 +2040,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
|
||||
|
||||
let intType = getSysType(c.graph, n.info, tyInt)
|
||||
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
|
||||
let seqType = n.typ.skipTypes(abstractVar+{tyStatic}-{tyTypeDesc})
|
||||
if seqType.kind == tySequence:
|
||||
var tmp = c.getTemp(intType)
|
||||
c.gABx(n, opcLdImmInt, tmp, n.len)
|
||||
|
||||
@@ -263,7 +263,7 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||
wrap2si(readLines, ioop)
|
||||
systemop getCurrentExceptionMsg
|
||||
systemop getCurrentException
|
||||
registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
|
||||
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, dirExists(getString(a, 0)))
|
||||
|
||||
@@ -15,7 +15,6 @@ proc dispatch(x: Base, params: ...) =
|
||||
|
||||
var disp = newNodeI(nkIfStmt, base.info)
|
||||
|
||||
var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
|
||||
let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
|
||||
let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
|
||||
|
||||
@@ -33,7 +32,7 @@ proc dispatch(x: Base, params: ...) =
|
||||
dispatchObject,
|
||||
newIntNode(nkIntLit, index)
|
||||
)
|
||||
getVTableCall.typ() = base.typ
|
||||
getVTableCall.typ() = getSysType(g, unknownLineInfo, tyPointer)
|
||||
var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
|
||||
var castNode = newTree(nkCast,
|
||||
newNodeIT(nkType, base.info, base.typ),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
=====================================================
|
||||
Nim -- a Compiler for Nim. https://nim-lang.org/
|
||||
|
||||
Copyright (C) 2006-2024 Andreas Rumpf. All rights reserved.
|
||||
Copyright (C) 2006-2025 Andreas Rumpf. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -51,7 +51,7 @@ Advanced options:
|
||||
--spellSuggest:num show at most `num >= 0` spelling suggestions on typos.
|
||||
if `num` is not specified (or `auto`), return
|
||||
an implementation defined set of suggestions.
|
||||
--hints:on|off|list. `on|off` enables or disables hints.
|
||||
--hints:on|off|list `on|off` enables or disables hints.
|
||||
`list` reports which hints are selected.
|
||||
--hint:X:on|off turn specific hint X on|off. `hint:X` means `hint:X:on`,
|
||||
as with similar flags. `all` is the set of all hints
|
||||
@@ -165,6 +165,7 @@ Advanced options:
|
||||
--verbosity:0|1|2|3 set Nim's verbosity level (1 is default)
|
||||
--errorMax:N stop compilation after N errors; 0 means unlimited
|
||||
--maxLoopIterationsVM:N set max iterations for all VM loops
|
||||
--maxCallDepthVM:N set max call depth in the VM
|
||||
--experimental:$1
|
||||
enable experimental language feature
|
||||
--legacy:$2
|
||||
|
||||
@@ -249,7 +249,7 @@ which will likely make your program crash at runtime.
|
||||
The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch.
|
||||
Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`.
|
||||
|
||||
When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors.
|
||||
When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors. It is influenced by the `--nimMainPrefix:prefix` switch, too.
|
||||
|
||||
|
||||
### Nim invocation example from C
|
||||
@@ -358,7 +358,7 @@ Memory management
|
||||
|
||||
In the previous sections, the `NimMain()` function reared its head. Since
|
||||
JavaScript already provides automatic memory management, you can freely pass
|
||||
objects between the two languages without problems. In C and derivate languages
|
||||
objects between the two languages without problems. In C and derivative languages
|
||||
you need to be careful about what you do and how you share memory. The
|
||||
previous examples only dealt with simple scalar values, but passing a Nim
|
||||
string to C, or reading back a C string in Nim already requires you to be
|
||||
|
||||
@@ -6264,7 +6264,7 @@ The default for symbols of entity `type`, `var`, `let` and `const`
|
||||
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
|
||||
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
|
||||
is defined in the same syntax-level scope, it will be `gensym` by default.
|
||||
This can be overriden by marking the routine as `inject`.
|
||||
This can be overridden by marking the routine as `inject`.
|
||||
|
||||
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
|
||||
|
||||
|
||||
@@ -2667,3 +2667,114 @@ proc nothing() =
|
||||
```
|
||||
|
||||
The current C(C++) backend implementation cannot generate code for gcc and for vcc at the same time. For example, `{.asmSyntax: "vcc".}` with the ICC compiler will not generate code with intel asm syntax, even though ICC can use both gcc-like and vcc-like asm.
|
||||
|
||||
Type-bound overloads
|
||||
====================
|
||||
|
||||
With the experimental option `--experimental:typeBoundOps`, each "root"
|
||||
nominal type (namely `object`, `enum`, `distinct`, direct `Foo = ref object`
|
||||
types as well as their generic versions) can have operations attached to it.
|
||||
Exported top-level routines declared in the same scope as a nominal type
|
||||
with a parameter having a type directly deriving from that nominal type (i.e.
|
||||
with `var`/`sink`/`typedesc` modifiers or being in a generic constraint)
|
||||
are considered "attached" to the respective nominal type.
|
||||
This applies to every parameter regardless of placement.
|
||||
|
||||
When a call to a symbol is openly overloaded and overload matching starts,
|
||||
for all arguments in the call that have already undergone type checking,
|
||||
routines with the same name attached to the root nominal type (if it exists)
|
||||
of each given argument are added as a candidate to the overload match.
|
||||
This also happens as arguments gradually get typed after every match to an overload.
|
||||
This is so that the only overloads considered out of scope are
|
||||
attached to the types of the given arguments, and that matches to
|
||||
`untyped` or missing parameters are not influenced by outside overloads.
|
||||
|
||||
If no overloads with a given name are in scope, then overload matching
|
||||
will not begin, and so type-bound overloads are not considered for that name.
|
||||
Similarly, if the only overloads with a given name require a parameter to be
|
||||
`untyped` or missing, then type-bound overloads will not be considered for
|
||||
the argument in that position.
|
||||
Generally this means that a "base" overload with a compliant signature should
|
||||
be in scope so that type-bound overloads can be used.
|
||||
|
||||
In the case of ambiguity between distinct local/imported and type-bound symbols
|
||||
in overload matching, type-bound symbols are considered as a less specific
|
||||
scope than imports.
|
||||
|
||||
An example with the `hash` interface in the standard library is as follows:
|
||||
|
||||
```nim
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
|
||||
proc hash*(a: Obj): Hash =
|
||||
$!(hash(a.x) &! hash(a.y))
|
||||
|
||||
# here both `==` and `hash` are attached to Obj
|
||||
# 1. they are both exported
|
||||
# 2. they are in the same scope as Obj
|
||||
# 3. they have parameters with types directly deriving from Obj
|
||||
# 4. Obj is nominal
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
from objs import Obj # objs.hash, objs.`==` not imported
|
||||
import std/tables
|
||||
# tables use `hash`, only using the overloads in `std/hashes` and
|
||||
# the ones in instantiation scope (in this case, there are none)
|
||||
|
||||
var t: Table[Obj, int]
|
||||
# because tables use `hash` and `==` in a compliant way,
|
||||
# the overloads bound to Obj are also considered, and in this case match best
|
||||
t[Obj(x: 3, y: 4, z: "debug")] = 34
|
||||
# if `hash` for all objects as in `std/hashes` was used, this would error:
|
||||
echo t[Obj(x: 3, y: 4, z: "ignored")] # 34
|
||||
```
|
||||
|
||||
Another example, this time with `$` and indirect imports:
|
||||
|
||||
```nim
|
||||
# foo.nim
|
||||
type Foo* = object
|
||||
x*, y*: int
|
||||
|
||||
proc `$`*(f: Foo): string =
|
||||
"Foo(" & $f.x & ", " & $f.y & ")"
|
||||
```
|
||||
|
||||
```nim
|
||||
# bar.nim
|
||||
import foo
|
||||
|
||||
proc makeFoo*(x, y: int): Foo =
|
||||
Foo(x: x, y: y)
|
||||
|
||||
proc useFoo*(f: Foo) =
|
||||
echo "used: ", f # directly calls `foo.$` from scope
|
||||
```
|
||||
|
||||
```nim
|
||||
# debugger.nim
|
||||
proc debug*[T](obj: T) =
|
||||
echo "debugging: ", obj # calls generic `$`
|
||||
```
|
||||
|
||||
```nim
|
||||
# main.nim
|
||||
{.experimental: "typeBoundOps".}
|
||||
import bar, debugger # `foo` not imported, so `foo.$` not in scope
|
||||
|
||||
let f = makeFoo(123, 456)
|
||||
useFoo(f) # used: Foo(123, 456)
|
||||
debug(f) # debugging: Foo(123, 456)
|
||||
```
|
||||
|
||||
@@ -770,11 +770,10 @@ div.topic {
|
||||
|
||||
div.search_results {
|
||||
background-color: var(--third-background);
|
||||
margin: 3vh 5vw;
|
||||
padding: 1em;
|
||||
border: 1px solid #4d4d4d;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
position: sticky;
|
||||
top: 1em;
|
||||
isolation: isolate;
|
||||
max-width: calc(100vw - 6em);
|
||||
z-index: 1;
|
||||
|
||||
@@ -30,28 +30,30 @@ a working NodeJS on `PATH`.
|
||||
Commands
|
||||
========
|
||||
|
||||
========================== ==========================
|
||||
p|pat|pattern <glob> run all the tests matching the given pattern
|
||||
all run all tests inside of category folders
|
||||
c|cat|category <category> run all the tests of a certain category
|
||||
r|run <test> run single test file
|
||||
html generate testresults.html from the database
|
||||
========================== ==========================
|
||||
|
||||
|
||||
Options
|
||||
=======
|
||||
|
||||
--print print results to the console
|
||||
--verbose print commands (compiling and running tests)
|
||||
--simulate see what tests would be run but don't run them (for debugging)
|
||||
--failing only show failing/ignored tests
|
||||
--targets:"c cpp js objc" run tests for specified targets (default: c)
|
||||
--nim:path use a particular nim executable (default: $PATH/nim)
|
||||
--directory:dir Change to directory dir before reading the tests or doing anything else.
|
||||
--colors:on|off Turn messages coloring on|off.
|
||||
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
||||
--megatest:on|off Enable or disable megatest. Default is on.
|
||||
--valgrind:on|off Enable or disable valgrind support. Default is on.
|
||||
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
|
||||
--print print results to the console
|
||||
--verbose print commands (compiling and running tests)
|
||||
--simulate see what tests would be run but don't run them (for debugging)
|
||||
--failing only show failing/ignored tests
|
||||
--targets:"c cpp js objc" run tests for specified targets (default: c)
|
||||
--nim:path use a particular nim executable (default: $PATH/nim)
|
||||
--directory:dir Change to directory dir before reading the tests or doing anything else.
|
||||
--colors:on|off Turn messages coloring on|off.
|
||||
--backendLogging:on|off Disable or enable backend logging. By default turned on.
|
||||
--megatest:on|off Enable or disable megatest. Default is on.
|
||||
--valgrind:on|off Enable or disable valgrind support. Default is on.
|
||||
--skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored
|
||||
|
||||
|
||||
Running a single test
|
||||
|
||||
13
koch.nim
13
koch.nim
@@ -11,7 +11,7 @@
|
||||
|
||||
const
|
||||
# examples of possible values for repos: Head, ea82b54
|
||||
NimbleStableCommit = "46e2ae13eeb95619a371a74d16efc5aff30ea371" # 0.16.2
|
||||
NimbleStableCommit = "123f97a5e4ee9ba35720c0869e19a047c43c797e" # 0.16.4
|
||||
AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
|
||||
ChecksumsStableCommit = "bd9bf4eaea124bf8d01e08f92ac1b14c6879d8d3"
|
||||
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
|
||||
@@ -159,13 +159,12 @@ proc bundleNimbleExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: NimbleStableCommit
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
|
||||
commit = commit, allowBundled = true)
|
||||
cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/checksums.git",
|
||||
commit = ChecksumsStableCommit, allowBundled = true) # or copy it from dist?
|
||||
cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/sat.git",
|
||||
commit = SatStableCommit, allowBundled = true)
|
||||
# installer.ini expects it under $nim/bin
|
||||
updateSubmodules(distDir / "nimble", allowBundled = true)
|
||||
nimCompile("dist/nimble/src/nimble.nim",
|
||||
options = "-d:release -d:nimNimbleBootstrap --noNimblePath " & args)
|
||||
options = "-d:release --noNimblePath " & args)
|
||||
const zippyTests = "dist/nimble/vendor/zippy/tests"
|
||||
if dirExists(zippyTests):
|
||||
removeDir(zippyTests)
|
||||
|
||||
proc bundleAtlasExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: AtlasStableCommit
|
||||
|
||||
@@ -44,6 +44,9 @@ runnableExamples:
|
||||
assert mcCounter.value == 3
|
||||
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
CacheSeq* = distinct string
|
||||
## Compile-time sequence of `NimNode`s.
|
||||
@@ -207,7 +210,7 @@ proc hasKey*(t: CacheTable; key: string): bool =
|
||||
mcTable["foo"] = newEmptyNode()
|
||||
# Will now be true since we inserted a value
|
||||
assert mcTable.hasKey("foo")
|
||||
discard "Implemented in vmops"
|
||||
raiseAssert "implemented in the vmops"
|
||||
|
||||
proc contains*(t: CacheTable; key: string): bool {.inline.} =
|
||||
## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator.
|
||||
|
||||
@@ -365,7 +365,7 @@ proc symBodyHash*(s: NimNode): string {.noSideEffect.} =
|
||||
## and owning module, but also implementation body. All procs/variables used in
|
||||
## the implementation of this symbol are hashed recursively as well, including
|
||||
## magics from system module.
|
||||
discard
|
||||
raiseAssert "implemented in the vmops"
|
||||
|
||||
proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.}
|
||||
## Version of `getTypeImpl` which takes a `typedesc`.
|
||||
@@ -1327,7 +1327,7 @@ proc `name=`*(someProc: NimNode; val: NimNode) =
|
||||
else: someProc[0] = val
|
||||
|
||||
proc params*(someProc: NimNode): NimNode =
|
||||
if someProc.kind == nnkProcTy:
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
someProc[0]
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1335,7 +1335,7 @@ proc params*(someProc: NimNode): NimNode =
|
||||
|
||||
proc `params=`* (someProc: NimNode; params: NimNode) =
|
||||
expectKind(params, nnkFormalParams)
|
||||
if someProc.kind == nnkProcTy:
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
someProc[0] = params
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1344,7 +1344,7 @@ proc `params=`* (someProc: NimNode; params: NimNode) =
|
||||
proc pragma*(someProc: NimNode): NimNode =
|
||||
## Get the pragma of a proc type.
|
||||
## These will be expanded.
|
||||
if someProc.kind == nnkProcTy:
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
result = someProc[1]
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1352,7 +1352,7 @@ proc pragma*(someProc: NimNode): NimNode =
|
||||
proc `pragma=`*(someProc: NimNode; val: NimNode) =
|
||||
## Set the pragma of a proc type.
|
||||
expectKind(val, {nnkEmpty, nnkPragma})
|
||||
if someProc.kind == nnkProcTy:
|
||||
if someProc.kind in {nnkProcTy, nnkIteratorTy}:
|
||||
someProc[1] = val
|
||||
else:
|
||||
someProc.expectRoutine
|
||||
@@ -1440,7 +1440,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
|
||||
## it.basename.ident == ident"foo")
|
||||
## ```
|
||||
block:
|
||||
var res: NimNode
|
||||
var res: NimNode = nil
|
||||
for it in n.children:
|
||||
if cond:
|
||||
res = it
|
||||
@@ -1508,6 +1508,7 @@ proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} =
|
||||
|
||||
proc hasArgOfName*(params: NimNode; name: string): bool =
|
||||
## Search `nnkFormalParams` for an argument.
|
||||
result = false
|
||||
expectKind(params, nnkFormalParams)
|
||||
for i in 1..<params.len:
|
||||
for j in 0..<params[i].len-2:
|
||||
@@ -1575,6 +1576,7 @@ proc extractTypeImpl(n: NimNode): NimNode =
|
||||
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
|
||||
|
||||
proc customPragmaNode(n: NimNode): NimNode =
|
||||
result = nil
|
||||
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
|
||||
let
|
||||
typ = n.getTypeInst()
|
||||
@@ -1730,7 +1732,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
|
||||
for i in 0 ..< args.len:
|
||||
result.add args[i]
|
||||
|
||||
proc getProjectPath*(): string = discard
|
||||
proc getProjectPath*(): string = raiseAssert "implemented in the vmops"
|
||||
## Returns the path to the currently compiling project.
|
||||
##
|
||||
## This is not to be confused with `system.currentSourcePath <system.html#currentSourcePath.t>`_
|
||||
@@ -1773,6 +1775,7 @@ proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
|
||||
|
||||
proc isExported*(n: NimNode): bool {.noSideEffect.} =
|
||||
## Returns whether the symbol is exported or not.
|
||||
raiseAssert "implemented in the vmops"
|
||||
|
||||
proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
|
||||
## returns a `nnkStmtList` containing the top-level doc comments and
|
||||
|
||||
@@ -24,7 +24,7 @@ type
|
||||
proc initRLock*(lock: var RLock) {.inline.} =
|
||||
## Initializes the given lock.
|
||||
when defined(posix):
|
||||
var a: SysLockAttr
|
||||
var a: SysLockAttr = default(SysLockAttr)
|
||||
initSysLockAttr(a)
|
||||
setSysLockType(a, SysLockType_Reentrant)
|
||||
initSysLock(lock, a.addr)
|
||||
|
||||
@@ -145,7 +145,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
|
||||
of 2: d = int(cast[ptr uint16](a +% n.offset)[])
|
||||
of 4: d = int(cast[ptr uint32](a +% n.offset)[])
|
||||
of 8: d = int(cast[ptr uint64](a +% n.offset)[])
|
||||
else: assert(false)
|
||||
else: raiseAssert "unreachable"
|
||||
return d
|
||||
|
||||
proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
@@ -158,7 +158,7 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
result = n.sons[n.len]
|
||||
|
||||
proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
|
||||
result.value = value
|
||||
result = Any(value: value)
|
||||
result.rawType = rawType
|
||||
|
||||
proc toAny*[T](x: var T): Any {.inline.} =
|
||||
@@ -169,7 +169,7 @@ proc toAny*[T](x: var T): Any {.inline.} =
|
||||
|
||||
proc kind*(x: Any): AnyKind {.inline.} =
|
||||
## Gets the type kind.
|
||||
result = AnyKind(ord(x.rawType.kind))
|
||||
result = cast[AnyKind](ord(x.rawType.kind))
|
||||
|
||||
proc size*(x: Any): int {.inline.} =
|
||||
## Returns the size of `x`'s type.
|
||||
@@ -178,12 +178,16 @@ proc size*(x: Any): int {.inline.} =
|
||||
proc baseTypeKind*(x: Any): AnyKind {.inline.} =
|
||||
## Gets the base type's kind. If `x` has no base type, `akNone` is returned.
|
||||
if x.rawType.base != nil:
|
||||
result = AnyKind(ord(x.rawType.base.kind))
|
||||
result = cast[AnyKind](ord(x.rawType.base.kind))
|
||||
else:
|
||||
result = akNone
|
||||
|
||||
proc baseTypeSize*(x: Any): int {.inline.} =
|
||||
## Returns the size of `x`'s base type. If `x` has no base type, 0 is returned.
|
||||
if x.rawType.base != nil:
|
||||
result = x.rawType.base.size
|
||||
else:
|
||||
result = 0
|
||||
|
||||
proc invokeNew*(x: Any) =
|
||||
## Performs `new(x)`. `x` needs to represent a `ref`.
|
||||
@@ -263,7 +267,7 @@ proc `[]`*(x: Any, i: int): Any =
|
||||
if i >=% cast[PGenSeq](s).len:
|
||||
raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
|
||||
return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base)
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc `[]=`*(x: Any, i: int, y: Any) =
|
||||
## Accessor for an any `x` that represents an array or a sequence.
|
||||
@@ -292,7 +296,7 @@ proc `[]=`*(x: Any, i: int, y: Any) =
|
||||
raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
|
||||
assert y.rawType == x.rawType.base
|
||||
genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType)
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc len*(x: Any): int =
|
||||
## `len` for an any `x` that represents an array or a sequence.
|
||||
@@ -308,14 +312,13 @@ proc len*(x: Any): int =
|
||||
result = 0
|
||||
else:
|
||||
result = pgenSeq.len
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
|
||||
proc base*(x: Any): Any =
|
||||
## Returns the base type of `x` (useful for inherited object types).
|
||||
result = Any(value: x.value)
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
|
||||
proc isNil*(x: Any): bool =
|
||||
## `isNil` for an `x` that represents a cstring, proc or
|
||||
@@ -367,7 +370,7 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
when false:
|
||||
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
|
||||
var ret: seq[tuple[name: cstring, any: Any]]
|
||||
var ret: seq[tuple[name: cstring, any: Any]] = @[]
|
||||
if t.kind == tyObject:
|
||||
while true:
|
||||
fieldsAux(p, t.node, ret)
|
||||
@@ -379,8 +382,9 @@ iterator fields*(x: Any): tuple[name: string, any: Any] =
|
||||
yield ($name, any)
|
||||
|
||||
proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode =
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkNone: assert(false)
|
||||
of nkNone: raiseAssert "unreachable"
|
||||
of nkSlot:
|
||||
if cmpNimIdentifier(n.name, name) == 0:
|
||||
result = n
|
||||
@@ -418,7 +422,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
|
||||
assert x.rawType.kind in {tyTuple, tyObject}
|
||||
let n = getFieldNode(x.value, t.node, fieldName)
|
||||
if n != nil:
|
||||
result.value = x.value +!! n.offset
|
||||
result = Any(value: x.value +!! n.offset)
|
||||
result.rawType = n.typ
|
||||
elif x.rawType.kind == tyObject and x.rawType.base != nil:
|
||||
return `[]`(newAny(x.value, x.rawType.base), fieldName)
|
||||
@@ -428,7 +432,7 @@ proc `[]`*(x: Any, fieldName: string): Any =
|
||||
proc `[]`*(x: Any): Any =
|
||||
## Dereference operator for `Any`. `x` needs to represent a ptr or a ref.
|
||||
assert x.rawType.kind in {tyRef, tyPtr}
|
||||
result.value = cast[ppointer](x.value)[]
|
||||
result = Any(value: cast[ppointer](x.value)[])
|
||||
result.rawType = x.rawType.base
|
||||
|
||||
proc `[]=`*(x, y: Any) =
|
||||
@@ -481,12 +485,12 @@ proc getBiggestInt*(x: Any): BiggestInt =
|
||||
of 2: result = int64(cast[ptr uint16](x.value)[])
|
||||
of 4: result = BiggestInt(cast[ptr int32](x.value)[])
|
||||
of 8: result = BiggestInt(cast[ptr int64](x.value)[])
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
of tyUInt: result = BiggestInt(cast[ptr uint](x.value)[])
|
||||
of tyUInt8: result = BiggestInt(cast[ptr uint8](x.value)[])
|
||||
of tyUInt16: result = BiggestInt(cast[ptr uint16](x.value)[])
|
||||
of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[])
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc setBiggestInt*(x: Any, y: BiggestInt) =
|
||||
## Sets the integer value of `x`. `x` needs to represent
|
||||
@@ -548,7 +552,7 @@ proc getBiggestUint*(x: Any): uint64 =
|
||||
of tyUInt16: result = uint64(cast[ptr uint16](x.value)[])
|
||||
of tyUInt32: result = uint64(cast[ptr uint32](x.value)[])
|
||||
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc setBiggestUint*(x: Any; y: uint64) =
|
||||
## Sets the unsigned integer value of `x`. `x` needs to represent an
|
||||
@@ -560,7 +564,7 @@ proc setBiggestUint*(x: Any; y: uint64) =
|
||||
of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y)
|
||||
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
|
||||
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc getChar*(x: Any): char =
|
||||
## Retrieves the `char` value out of `x`. `x` needs to represent a `char`.
|
||||
@@ -577,8 +581,8 @@ proc getBool*(x: Any): bool =
|
||||
proc skipRange*(x: Any): Any =
|
||||
## Skips the range information of `x`.
|
||||
assert x.rawType.kind == tyRange
|
||||
result = Any(value: x.value)
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
proc getEnumOrdinal*(x: Any, name: string): int =
|
||||
## Gets the enum field ordinal from `name`. `x` needs to represent an enum
|
||||
@@ -640,7 +644,7 @@ proc getBiggestFloat*(x: Any): BiggestFloat =
|
||||
of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[])
|
||||
of tyFloat32: result = BiggestFloat(cast[ptr float32](x.value)[])
|
||||
of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[])
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc setBiggestFloat*(x: Any, y: BiggestFloat) =
|
||||
## Sets the float value of `x`. `x` needs to represent
|
||||
@@ -649,7 +653,7 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) =
|
||||
of tyFloat: cast[ptr float](x.value)[] = y
|
||||
of tyFloat32: cast[ptr float32](x.value)[] = y.float32
|
||||
of tyFloat64: cast[ptr float64](x.value)[] = y
|
||||
else: assert false
|
||||
else: raiseAssert "unreachable"
|
||||
|
||||
proc getString*(x: Any): string =
|
||||
## Retrieves the `string` value out of `x`. `x` needs to represent a `string`.
|
||||
@@ -682,7 +686,7 @@ iterator elements*(x: Any): int =
|
||||
let typ = x.rawType
|
||||
let p = x.value
|
||||
# "typ.slots.len" field is for sets the "first" field
|
||||
var u: int64
|
||||
var u: int64 = 0'i64
|
||||
case typ.size
|
||||
of 1: u = int64(cast[ptr uint8](p)[])
|
||||
of 2: u = int64(cast[ptr uint16](p)[])
|
||||
|
||||
@@ -82,7 +82,7 @@ proc diffCodes(aText: string; h: var Table[string, int]): DiffData =
|
||||
## `trimSpace` ignore leading and trailing space characters
|
||||
## Returns a array of integers.
|
||||
var lastUsedCode = h.len
|
||||
result.data = newSeq[int]()
|
||||
result = DiffData(data: newSeq[int]())
|
||||
for s in aText.splitLines:
|
||||
if h.contains s:
|
||||
result.data.add h[s]
|
||||
@@ -123,7 +123,7 @@ proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, uppe
|
||||
## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
|
||||
## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
|
||||
## Returns a MiddleSnakeData record containing x,y and u,v.
|
||||
|
||||
result = Smsrd()
|
||||
let max = dataA.len + dataB.len + 1
|
||||
|
||||
let downK = lowerA - lowerB # the k-line to start the forward search
|
||||
@@ -250,6 +250,7 @@ proc lcs(dataA: var DiffData; lowerA, upperA: int; dataB: var DiffData; lowerB,
|
||||
proc createDiffs(dataA, dataB: DiffData): seq[Item] =
|
||||
## Scan the tables of which lines are inserted and deleted,
|
||||
## producing an edit script in forward order.
|
||||
result = @[]
|
||||
var startA = 0
|
||||
var startB = 0
|
||||
var lineA = 0
|
||||
|
||||
@@ -74,7 +74,14 @@ when defined(nimPreviewSlimSystem):
|
||||
export options
|
||||
|
||||
type
|
||||
Regex* = ref object
|
||||
RegexDesc* = object
|
||||
pattern*: string
|
||||
pcreObj: ptr pcre.Pcre ## not nil
|
||||
pcreExtra: ptr pcre.ExtraData ## nil
|
||||
|
||||
captureNameToId: Table[string, int]
|
||||
|
||||
Regex* = ref RegexDesc
|
||||
## Represents the pattern that things are matched against, constructed with
|
||||
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
|
||||
## comment".`
|
||||
@@ -145,11 +152,6 @@ type
|
||||
## `DOLLAR_ENDONLY`, `FIRSTLINE`, `NO_AUTO_CAPTURE`,
|
||||
## `JAVASCRIPT_COMPAT`, `U`, `NO_STUDY`. In other PCRE wrappers, you
|
||||
## will need to pass these as separate flags to PCRE.
|
||||
pattern*: string
|
||||
pcreObj: ptr pcre.Pcre ## not nil
|
||||
pcreExtra: ptr pcre.ExtraData ## nil
|
||||
|
||||
captureNameToId: Table[string, int]
|
||||
|
||||
RegexMatch* = object
|
||||
## Usually seen as Option[RegexMatch], it represents the result of an
|
||||
@@ -216,12 +218,28 @@ type
|
||||
## for whatever reason. The message contains the error
|
||||
## code.
|
||||
|
||||
proc destroyRegex(pattern: Regex) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
when defined(gcDestructors):
|
||||
when defined(nimAllowNonVarDestructor) and defined(nimPreviewNonVarDestructor):
|
||||
proc `=destroy`(pattern: RegexDesc) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
else:
|
||||
proc `=destroy`(pattern: var RegexDesc) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
else:
|
||||
proc destroyRegex(pattern: Regex) =
|
||||
`=destroy`(pattern.pattern)
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
`=destroy`(pattern.captureNameToId)
|
||||
|
||||
proc getinfo[T](pattern: Regex, opt: cint): T =
|
||||
let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
|
||||
@@ -251,7 +269,10 @@ proc getNameToNumberTable(pattern: Regex): Table[string, int] =
|
||||
result[name] = num
|
||||
|
||||
proc initRegex(pattern: string, flags: int, study = true): Regex =
|
||||
new(result, destroyRegex)
|
||||
when defined(gcDestructors):
|
||||
result = Regex()
|
||||
else:
|
||||
new(result, destroyRegex)
|
||||
result.pattern = pattern
|
||||
|
||||
var errorMsg: cstring
|
||||
|
||||
@@ -1490,7 +1490,9 @@ proc clearInterval*(i: Interval) {.importc, nodecl.}
|
||||
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
|
||||
proc addEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
|
||||
proc dispatchEvent*(et: EventTarget, ev: Event)
|
||||
proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event))
|
||||
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), useCapture: bool = false)
|
||||
proc removeEventListener*(et: EventTarget, ev: cstring, cb: proc(ev: Event), options: AddEventListenerOptions)
|
||||
|
||||
|
||||
# Window "methods"
|
||||
proc alert*(w: Window, msg: cstring)
|
||||
|
||||
@@ -15,6 +15,7 @@ __TINYC__
|
||||
__clang__
|
||||
__AVR__
|
||||
__arm__
|
||||
__riscv
|
||||
__EMSCRIPTEN__
|
||||
*/
|
||||
|
||||
@@ -469,10 +470,17 @@ typedef char* NCSTRING;
|
||||
|
||||
#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
|
||||
|
||||
#define STRING_LITERAL(name, str, length) \
|
||||
static const struct { \
|
||||
TGenericSeq Sup; \
|
||||
NIM_CHAR data[(length) + 1]; \
|
||||
} name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
|
||||
|
||||
/* declared size of a sequence/variable length array: */
|
||||
#if defined(__cplusplus) && defined(__clang__)
|
||||
# define SEQ_DECL_SIZE 1
|
||||
#elif defined(__GNUC__) || defined(_MSC_VER)
|
||||
#elif defined(__GNUC__) || defined(_MSC_VER) || defined(__TINYC__) || \
|
||||
(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) // C99
|
||||
# define SEQ_DECL_SIZE /* empty is correct! */
|
||||
#else
|
||||
# define SEQ_DECL_SIZE 1000000
|
||||
@@ -579,8 +587,8 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "P
|
||||
#define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
|
||||
|
||||
#if NIM_INTBITS == 32
|
||||
#if defined(__arm__) && defined(__GNUC__)
|
||||
/* arm-none-eabi-gcc targets defines int32_t as long int */
|
||||
#if (defined(__arm__) || defined(__riscv)) && defined(__GNUC__)
|
||||
/* arm-none-eabi-gcc and riscv32-unknown-elf-gcc targets define int32_t as long int */
|
||||
#define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res)
|
||||
#define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res)
|
||||
#define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res)
|
||||
|
||||
@@ -301,6 +301,8 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
|
||||
g.kind = nimGetKeyword(id)
|
||||
elif isKeyword(keywords, id) >= 0:
|
||||
g.kind = gtKeyword
|
||||
else:
|
||||
g.kind = gtIdentifier
|
||||
of '0':
|
||||
inc(pos)
|
||||
case g.buf[pos]
|
||||
|
||||
@@ -503,7 +503,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {.
|
||||
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
||||
|
||||
|
||||
proc exitnow*(code: int) {.importc: "_exit", header: "<unistd.h>".}
|
||||
proc exitnow*(code: cint) {.importc: "_exit", header: "<unistd.h>", noreturn.}
|
||||
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
||||
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
||||
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
||||
|
||||
@@ -1300,7 +1300,8 @@ else:
|
||||
# `rwlist` associated with file descriptor MUST BE emptied before
|
||||
# dispatching callback (See https://github.com/nim-lang/Nim/issues/5128),
|
||||
# or it can be possible to fall into endless cycle.
|
||||
var curList: seq[Callback]
|
||||
result = (0, 0)
|
||||
var curList: seq[Callback] = @[]
|
||||
|
||||
let selector = getGlobalDispatcher().selector
|
||||
withData(selector, fd.int, fdData):
|
||||
@@ -1352,7 +1353,7 @@ else:
|
||||
# {Event.Timer, Event.Signal, Event.Process, Event.Vnode}.
|
||||
# There can be only one callback registered with one descriptor,
|
||||
# so there is no need to iterate over list.
|
||||
var curList: seq[Callback]
|
||||
var curList: seq[Callback] = @[]
|
||||
|
||||
withData(p.selector, fd.int, adata) do:
|
||||
curList = move adata.readList
|
||||
@@ -1550,7 +1551,7 @@ else:
|
||||
var retFuture = newFuture[void]("sendTo")
|
||||
|
||||
# we will preserve address in our stack
|
||||
var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes
|
||||
var staddr {.noinit.} : array[128, char] # SOCKADDR_STORAGE size is 128 bytes
|
||||
var stalen = saddrLen
|
||||
zeroMem(addr(staddr[0]), 128)
|
||||
copyMem(addr(staddr[0]), saddr, saddrLen)
|
||||
@@ -1604,7 +1605,7 @@ else:
|
||||
client: AsyncFD]]("acceptAddr")
|
||||
proc cb(sock: AsyncFD): bool {.gcsafe.} =
|
||||
result = true
|
||||
var sockAddress: Sockaddr_storage
|
||||
var sockAddress: Sockaddr_storage = default(Sockaddr_storage)
|
||||
var addrLen = sizeof(sockAddress).SockLen
|
||||
var client =
|
||||
when declared(accept4):
|
||||
@@ -1696,12 +1697,12 @@ proc drain*(timeout = 500) =
|
||||
## Waits for completion of **all** events and processes them. Raises `ValueError`
|
||||
## if there are no pending operations. In contrast to `poll` this
|
||||
## processes as many events as are available until the timeout has elapsed.
|
||||
var curTimeout = timeout
|
||||
let start = now()
|
||||
var elapsed = 0
|
||||
let start = getMonoTime()
|
||||
while hasPendingOperations():
|
||||
discard runOnce(curTimeout)
|
||||
curTimeout -= (now() - start).inMilliseconds.int
|
||||
if curTimeout < 0:
|
||||
discard runOnce(timeout - elapsed)
|
||||
elapsed = int (getMonoTime() - start).inMilliseconds
|
||||
if elapsed >= timeout:
|
||||
break
|
||||
|
||||
proc poll*(timeout = 500) =
|
||||
@@ -2045,7 +2046,7 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
|
||||
elif defined(zephyr) or defined(freertos):
|
||||
result = FD_MAX
|
||||
else:
|
||||
var fdLim: RLimit
|
||||
var fdLim: RLimit = default(RLimit)
|
||||
if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
|
||||
raiseOSError(osLastError())
|
||||
result = int(fdLim.rlim_cur) - 1
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
|
||||
import std/[os, sets, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
|
||||
|
||||
import system/stacktraces
|
||||
|
||||
@@ -293,57 +293,47 @@ template getFilenameProcname(entry: StackTraceEntry): (string, string) =
|
||||
else:
|
||||
($entry.filename, $entry.procname)
|
||||
|
||||
proc getHint(entry: StackTraceEntry): string =
|
||||
## We try to provide some hints about stack trace entries that the user
|
||||
## may not be familiar with, in particular calls inside the stdlib.
|
||||
|
||||
proc format(entry: StackTraceEntry): string =
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
let left = "$#($#)" % [filename, $entry.line]
|
||||
result = spaces(2) & "$# $#\n" % [left, procname]
|
||||
|
||||
result = ""
|
||||
if procname == "processPendingCallbacks":
|
||||
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
|
||||
return "Executes pending callbacks"
|
||||
elif procname == "poll":
|
||||
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
|
||||
return "Processes asynchronous completion events"
|
||||
|
||||
if procname.endsWith(NimAsyncContinueSuffix):
|
||||
if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0:
|
||||
return "Resumes an async procedure"
|
||||
proc isInternal(entry: StackTraceEntry): bool =
|
||||
# --excessiveStackTrace:off
|
||||
const internals = [
|
||||
"asyncdispatch.nim",
|
||||
"asyncfutures.nim",
|
||||
"threadimpl.nim", # XXX ?
|
||||
]
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
for line in internals:
|
||||
if filename.endsWith line:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
|
||||
result = ""
|
||||
when defined(nimStackTraceOverride):
|
||||
let entries = addDebuggingInfo(stackTraceEntries)
|
||||
else:
|
||||
let entries = stackTraceEntries
|
||||
|
||||
result = ""
|
||||
# Find longest filename & line number combo for alignment purposes.
|
||||
var longestLeft = 0
|
||||
for entry in entries:
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
if procname == "": continue
|
||||
|
||||
let leftLen = filename.len + len($entry.line)
|
||||
if leftLen > longestLeft:
|
||||
longestLeft = leftLen
|
||||
|
||||
# Format the entries.
|
||||
for entry in entries:
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
if procname == "" and entry.line == reraisedFromBegin:
|
||||
break
|
||||
|
||||
let left = "$#($#)" % [filename, $entry.line]
|
||||
result.add((spaces(2) & "$# $#\n") % [
|
||||
left,
|
||||
procname
|
||||
])
|
||||
let hint = getHint(entry)
|
||||
if hint.len > 0:
|
||||
result.add(spaces(4) & "## " & hint & "\n")
|
||||
var seenEntries = initHashSet[StackTraceEntry]()
|
||||
let L = entries.len-1
|
||||
var i = L
|
||||
var j = 0
|
||||
while i >= 0:
|
||||
if entries[i].line == reraisedFromBegin or i == 0:
|
||||
j = i + int(i != 0)
|
||||
while j < L:
|
||||
if entries[j].line == reraisedFromBegin:
|
||||
break
|
||||
if entries[j].line >= 0 and not isInternal(entries[j]):
|
||||
# this skips recursive calls sadly
|
||||
if entries[j] notin seenEntries:
|
||||
result.add format(entries[j])
|
||||
seenEntries.incl entries[j]
|
||||
inc j
|
||||
dec i
|
||||
|
||||
proc injectStacktrace[T](future: Future[T]) =
|
||||
when not defined(release):
|
||||
@@ -356,7 +346,6 @@ proc injectStacktrace[T](future: Future[T]) =
|
||||
let start = exceptionMsg.find(header)
|
||||
exceptionMsg = exceptionMsg[0..<start]
|
||||
|
||||
|
||||
var newMsg = exceptionMsg & header
|
||||
|
||||
let entries = getStackTraceEntries(future.error)
|
||||
|
||||
@@ -146,6 +146,7 @@ proc respondError(req: Request, code: HttpCode): Future[void] =
|
||||
result = req.client.send(msg)
|
||||
|
||||
proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
|
||||
result = default(tuple[orig: string, major, minor: int])
|
||||
var i = protocol.skipIgnoreCase("HTTP/")
|
||||
if i != 5:
|
||||
raise newException(ValueError, "Invalid request protocol. Got: " &
|
||||
|
||||
@@ -22,36 +22,39 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN
|
||||
result = newCall(theProc, args)
|
||||
result.copyLineInfo(fromNode)
|
||||
|
||||
template createCb(retFutureSym, iteratorNameSym,
|
||||
strName, identName, futureVarCompletions: untyped) =
|
||||
type
|
||||
ClosureIt[T] = iterator(f: Future[T]): owned(FutureBase)
|
||||
|
||||
template createCb(futTyp, strName, identName, futureVarCompletions: untyped) =
|
||||
bind finished
|
||||
var nameIterVar = iteratorNameSym
|
||||
proc identName {.closure, stackTrace: off.} =
|
||||
{.push stackTrace: off.}
|
||||
proc identName(fut: Future[futTyp], it: ClosureIt[futTyp]) {.effectsOf: it.} =
|
||||
try:
|
||||
if not nameIterVar.finished:
|
||||
var next = nameIterVar()
|
||||
if not it.finished:
|
||||
var next = it(fut)
|
||||
# Continue while the yielded future is already finished.
|
||||
while (not next.isNil) and next.finished:
|
||||
next = nameIterVar()
|
||||
if nameIterVar.finished:
|
||||
next = it(fut)
|
||||
if it.finished:
|
||||
break
|
||||
|
||||
if next == nil:
|
||||
if not retFutureSym.finished:
|
||||
if not fut.finished:
|
||||
let msg = "Async procedure ($1) yielded `nil`, are you await'ing a `nil` Future?"
|
||||
raise newException(AssertionDefect, msg % strName)
|
||||
else:
|
||||
{.gcsafe.}:
|
||||
next.addCallback cast[proc() {.closure, gcsafe.}](identName)
|
||||
next.addCallback(cast[proc() {.closure, gcsafe.}](proc =
|
||||
identName(fut, it)))
|
||||
except:
|
||||
futureVarCompletions
|
||||
if retFutureSym.finished:
|
||||
if fut.finished:
|
||||
# Take a look at tasyncexceptions for the bug which this fixes.
|
||||
# That test explains it better than I can here.
|
||||
raise
|
||||
else:
|
||||
retFutureSym.fail(getCurrentException())
|
||||
identName()
|
||||
fut.fail(getCurrentException())
|
||||
{.pop.}
|
||||
|
||||
proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode): NimNode =
|
||||
result = newNimNode(nnkStmtList, fromNode)
|
||||
@@ -68,7 +71,7 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode
|
||||
)
|
||||
)
|
||||
|
||||
proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
|
||||
proc processBody(ctx: Context; node, needsCompletionSym, retFutParamSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
|
||||
result = node
|
||||
case node.kind
|
||||
of nnkReturnStmt:
|
||||
@@ -80,14 +83,14 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
|
||||
ctx.hasRet = true
|
||||
if node[0].kind == nnkEmpty:
|
||||
if ctx.inTry == 0:
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, newIdentNode("result"))
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym, newIdentNode("result"))
|
||||
else:
|
||||
result.add newAssignment(needsCompletionSym, newLit(true))
|
||||
else:
|
||||
let x = processBody(ctx, node[0], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
let x = processBody(ctx, node[0], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
if x.kind == nnkYieldStmt: result.add x
|
||||
elif ctx.inTry == 0:
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, x)
|
||||
result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym, x)
|
||||
else:
|
||||
result.add newAssignment(newIdentNode("result"), x)
|
||||
result.add newAssignment(needsCompletionSym, newLit(true))
|
||||
@@ -100,10 +103,10 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
|
||||
of nnkTryStmt:
|
||||
if result[^1].kind == nnkFinally:
|
||||
inc ctx.inTry
|
||||
result[0] = processBody(ctx, result[0], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
result[0] = processBody(ctx, result[0], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
dec ctx.inTry
|
||||
for i in 1 ..< result.len:
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
if ctx.inTry == 0 and ctx.hasRet:
|
||||
let finallyNode = copyNimNode(result[^1])
|
||||
let stmtNode = newNimNode(nnkStmtList)
|
||||
@@ -111,7 +114,7 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
|
||||
stmtNode.add child
|
||||
stmtNode.add newIfStmt(
|
||||
( needsCompletionSym,
|
||||
newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym,
|
||||
newCallWithLineInfo(node, newIdentNode("complete"), retFutParamSym,
|
||||
newIdentNode("result")
|
||||
)
|
||||
)
|
||||
@@ -120,10 +123,10 @@ proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode,
|
||||
result[^1] = finallyNode
|
||||
else:
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
else:
|
||||
for i in 0 ..< result.len:
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
result[i] = processBody(ctx, result[i], needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
|
||||
# echo result.repr
|
||||
|
||||
@@ -167,7 +170,8 @@ template await*[T](f: Future[T]): auto {.used.} =
|
||||
when compiles(yieldFuture):
|
||||
var internalTmpFuture: FutureBase = f
|
||||
yield internalTmpFuture
|
||||
(cast[typeof(f)](internalTmpFuture)).read()
|
||||
{.line: instantiationInfo(fullPaths = true).}:
|
||||
(cast[typeof(f)](internalTmpFuture)).read()
|
||||
else:
|
||||
macro errorAsync(futureError: Future[T]) =
|
||||
error(
|
||||
@@ -218,6 +222,7 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
elif returnType.kind == nnkEmpty:
|
||||
baseType = returnType
|
||||
else:
|
||||
baseType = nil
|
||||
verifyReturnType(repr(returnType), returnType)
|
||||
|
||||
let futureVarIdents = getFutureVarIdents(prc.params)
|
||||
@@ -228,29 +233,21 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
# transformation even more complex.
|
||||
let body2 = extractDocCommentsAndRunnables(prc.body)
|
||||
|
||||
# -> var retFuture = newFuture[T]()
|
||||
var retFutureSym = genSym(nskVar, "retFuture")
|
||||
var subRetType =
|
||||
if returnType.kind == nnkEmpty: newIdentNode("void")
|
||||
else: baseType
|
||||
outerProcBody.add(
|
||||
newVarStmt(retFutureSym,
|
||||
newCall(
|
||||
newNimNode(nnkBracketExpr, prc.body).add(
|
||||
newIdentNode("newFuture"),
|
||||
subRetType),
|
||||
newLit(prcName)))) # Get type from return type of this proc
|
||||
let retFutParamSym = genSym(nskParam, "retFutParamSym")
|
||||
|
||||
# -> iterator nameIter(): FutureBase {.closure.} =
|
||||
# -> iterator nameIter(retFutParam: Future[T]): FutureBase {.closure.} =
|
||||
# -> {.push warning[resultshadowed]: off.}
|
||||
# -> var result: T
|
||||
# -> {.pop.}
|
||||
# -> <proc_body>
|
||||
# -> complete(retFuture, result)
|
||||
# -> complete(retFutParam, result)
|
||||
var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)")
|
||||
var needsCompletionSym = genSym(nskVar, "needsCompletion")
|
||||
var ctx = Context()
|
||||
var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutureSym, futureVarIdents)
|
||||
var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutParamSym, futureVarIdents)
|
||||
# don't do anything with forward bodies (empty)
|
||||
if procBody.kind != nnkEmpty:
|
||||
# fix #13899, defer should not escape its original scope
|
||||
@@ -274,9 +271,11 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
|
||||
var `needsCompletionSym` = false
|
||||
procBody.add quote do:
|
||||
complete(`retFutureSym`, `resultIdent`)
|
||||
complete(`retFutParamSym`, `resultIdent`)
|
||||
|
||||
var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase)],
|
||||
var retFutureTyp = newNimNode(nnkBracketExpr, prc).add(newIdentNode("Future")).add(subRetType)
|
||||
var retFutureParam = newNimNode(nnkIdentDefs, prc).add(retFutParamSym).add(retFutureTyp).add(newEmptyNode())
|
||||
var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase), retFutureParam],
|
||||
procBody, nnkIteratorDef)
|
||||
closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom = prc.body)
|
||||
closureIterator.addPragma(newIdentNode("closure"))
|
||||
@@ -286,17 +285,31 @@ proc asyncSingleProc(prc: NimNode): NimNode =
|
||||
closureIterator.addPragma(newIdentNode("gcsafe"))
|
||||
outerProcBody.add(closureIterator)
|
||||
|
||||
# -> createCb(retFuture)
|
||||
# -> createCb()
|
||||
# NOTE: The NimAsyncContinueSuffix is checked for in asyncfutures.nim to produce
|
||||
# friendlier stack traces:
|
||||
var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix)
|
||||
var procCb = getAst createCb(retFutureSym, iteratorNameSym,
|
||||
newStrLitNode(prcName),
|
||||
cbName,
|
||||
createFutureVarCompletions(futureVarIdents, nil)
|
||||
)
|
||||
var procCb = getAst createCb(
|
||||
subRetType,
|
||||
newStrLitNode(prcName),
|
||||
cbName,
|
||||
createFutureVarCompletions(futureVarIdents, nil)
|
||||
)
|
||||
outerProcBody.add procCb
|
||||
|
||||
# -> var retFuture = newFuture[T]()
|
||||
let retFutureSym = genSym(nskVar, "retFuture")
|
||||
outerProcBody.add(
|
||||
newVarStmt(retFutureSym,
|
||||
newCall(
|
||||
newNimNode(nnkBracketExpr, prc.body).add(
|
||||
newIdentNode("newFuture"),
|
||||
subRetType),
|
||||
newLit(prcName)))) # Get type from return type of this proc
|
||||
|
||||
# -> cb(retFuture, nameIter)
|
||||
outerProcBody.add newCall(cbName, retFutureSym, iteratorNameSym)
|
||||
|
||||
# -> return retFuture
|
||||
outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
|
||||
|
||||
|
||||
@@ -850,7 +850,7 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
|
||||
var
|
||||
it = aiList
|
||||
success = false
|
||||
lastException: ref Exception
|
||||
lastException: ref Exception = nil
|
||||
|
||||
while it != nil:
|
||||
let fut = sendTo(socket.fd.AsyncFD, cstring(data), len(data), it.ai_addr,
|
||||
@@ -915,16 +915,16 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
|
||||
"Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
|
||||
assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
|
||||
assert(size == len(data.mget()),
|
||||
"`date` was not initialized correctly. `size` != `len(data.mget())`")
|
||||
"`data` was not initialized correctly. `size` != `len(data.mget())`")
|
||||
assert(46 == len(address.mget()),
|
||||
"`address` was not initialized correctly. 46 != `len(address.mget())`")
|
||||
|
||||
case socket.domain
|
||||
of AF_INET6:
|
||||
var sAddr: Sockaddr_in6
|
||||
var sAddr: Sockaddr_in6 = default(Sockaddr_in6)
|
||||
adaptRecvFromToDomain(AF_INET6)
|
||||
of AF_INET:
|
||||
var sAddr: Sockaddr_in
|
||||
var sAddr: Sockaddr_in = default(Sockaddr_in)
|
||||
adaptRecvFromToDomain(AF_INET)
|
||||
else:
|
||||
raise newException(ValueError, "Unknown socket address family")
|
||||
|
||||
@@ -187,7 +187,7 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
|
||||
if e.len <= lineLen or newLine.len == 0:
|
||||
return e
|
||||
result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0)))
|
||||
var i, j, k, b: int
|
||||
var i, j, k, b: int = 0
|
||||
let nd = e.len - lineLen
|
||||
while j < nd:
|
||||
cpy(i + lineLen, e, j)
|
||||
@@ -197,6 +197,7 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
|
||||
|
||||
proc initDecodeTable*(): array[256, char] =
|
||||
# computes a decode table at compile time
|
||||
result = default(array[256, char])
|
||||
for i in 0 ..< 256:
|
||||
let ch = char(i)
|
||||
var code = invalidChar
|
||||
@@ -219,6 +220,7 @@ proc decode*(s: string): string =
|
||||
runnableExamples:
|
||||
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
|
||||
result = ""
|
||||
if s.len == 0: return
|
||||
|
||||
proc decodeSize(size: int): int =
|
||||
|
||||
@@ -83,6 +83,8 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||
else:
|
||||
if methodNone notin allowedMethods:
|
||||
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
||||
else:
|
||||
result = ""
|
||||
|
||||
iterator decodeData*(data: string): tuple[key, value: string] =
|
||||
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||
|
||||
@@ -533,5 +533,5 @@ proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (
|
||||
## Creates a new `CritBitTree` that contains the given `items`.
|
||||
runnableExamples:
|
||||
doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void]
|
||||
|
||||
result = default(CritBitTree[void])
|
||||
for item in items: result.incl item
|
||||
|
||||
@@ -91,7 +91,7 @@ proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] =
|
||||
echo typeof(extractRGB(a))
|
||||
echo extractRGB(b)
|
||||
echo typeof(extractRGB(b))
|
||||
|
||||
result = default(tuple[r, g, b: range[0..255]])
|
||||
result.r = a.int shr 16 and 0xff
|
||||
result.g = a.int shr 8 and 0xff
|
||||
result.b = a.int and 0xff
|
||||
|
||||
@@ -33,7 +33,7 @@ runnableExamples:
|
||||
{.push checks: off, line_dir: off, stack_trace: off, debugger: off.}
|
||||
# the user does not want to trace a part of the standard library!
|
||||
|
||||
import std/[math, strformat]
|
||||
import std/[math, strformat, strutils]
|
||||
|
||||
type
|
||||
Complex*[T: SomeFloat] = object
|
||||
|
||||
@@ -65,10 +65,10 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
|
||||
header: "<stdio.h>".}
|
||||
|
||||
var f: File
|
||||
var f: File = default(File)
|
||||
if f.open("/proc/loadavg"):
|
||||
var b: float
|
||||
var busy, total: int
|
||||
var busy, total: int = 0
|
||||
fscanf(f,"%lf %lf %lf %ld/%ld",
|
||||
addr b, addr b, addr b, addr busy, addr total)
|
||||
f.close()
|
||||
|
||||
@@ -155,6 +155,8 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
|
||||
signal(w.taskArrived)
|
||||
blockUntil(w.taskStarted)
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc cleanFlowVars(w: ptr Worker) =
|
||||
let q = addr(w.q)
|
||||
@@ -277,7 +279,7 @@ proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
|
||||
## to be able to wait on, -1 is returned.
|
||||
##
|
||||
## **Note:** This results in non-deterministic behaviour and should be avoided.
|
||||
var ai: AwaitInfo
|
||||
var ai: AwaitInfo = AwaitInfo()
|
||||
ai.cv.initSemaphore()
|
||||
var conflicts = 0
|
||||
result = -1
|
||||
|
||||
@@ -34,11 +34,13 @@ func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} =
|
||||
assert startsWith(cstring"Hello", cstring"")
|
||||
|
||||
when nimvm:
|
||||
result = false
|
||||
startsWithImpl(s, prefix)
|
||||
else:
|
||||
when defined(js):
|
||||
result = jsStartsWith(s, prefix)
|
||||
else:
|
||||
result = false
|
||||
var i = 0
|
||||
while true:
|
||||
if prefix[i] == '\0': return true
|
||||
@@ -55,11 +57,13 @@ func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} =
|
||||
assert endsWith(cstring"Hello", cstring"")
|
||||
|
||||
when nimvm:
|
||||
result = false
|
||||
endsWithImpl(s, suffix)
|
||||
else:
|
||||
when defined(js):
|
||||
result = jsEndsWith(s, suffix)
|
||||
else:
|
||||
result = false
|
||||
let slen = s.len
|
||||
var i = 0
|
||||
var j = slen - len(suffix)
|
||||
|
||||
@@ -90,6 +90,7 @@ proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle =
|
||||
## pragma does. Returns nil if the library could not be loaded.
|
||||
##
|
||||
## .. warning:: this proc uses the GC and so cannot be used to load the GC.
|
||||
result = default(LibHandle)
|
||||
var candidates = newSeq[string]()
|
||||
libCandidates(pattern, candidates)
|
||||
for c in candidates:
|
||||
|
||||
@@ -464,7 +464,7 @@ else:
|
||||
var outLen = csize_t len(result)
|
||||
var src = cstring(s)
|
||||
var dst = cstring(result)
|
||||
var iconvres: csize_t
|
||||
var iconvres: csize_t = csize_t(0)
|
||||
while inLen > 0:
|
||||
iconvres = iconv(c, addr src, addr inLen, addr dst, addr outLen)
|
||||
if iconvres == high(csize_t):
|
||||
|
||||
@@ -55,7 +55,7 @@ when useBuiltinSwap:
|
||||
## We have to use `copyMem` here instead of a simple dereference because they
|
||||
## may point to a unaligned address. A sufficiently smart compiler _should_
|
||||
## be able to elide them when they're not necessary.
|
||||
var tmp: T
|
||||
var tmp: T = default(T)
|
||||
copyMem(addr tmp, inp, sizeof(T))
|
||||
tmp = op(tmp)
|
||||
copyMem(outp, addr tmp, sizeof(T))
|
||||
|
||||
@@ -122,6 +122,7 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
|
||||
when Hash.sizeof < 8:
|
||||
result = hiXorLoFallback64(a, b)
|
||||
elif defined(gcc) or defined(llvm_gcc) or defined(clang):
|
||||
result = uint64(0)
|
||||
{.emit: """__uint128_t r = `a`; r *= `b`; `result` = (r >> 64) ^ r;""".}
|
||||
elif defined(windows) and not defined(tcc):
|
||||
proc umul128(a, b: uint64, c: ptr uint64): uint64 {.importc: "_umul128", header: "intrin.h".}
|
||||
@@ -316,7 +317,7 @@ proc murmurHash(x: openArray[byte]): Hash =
|
||||
stepSize = 4 # 32-bit
|
||||
n = size div stepSize
|
||||
var
|
||||
h1: uint32
|
||||
h1: uint32 = uint32(0)
|
||||
i = 0
|
||||
|
||||
|
||||
@@ -328,7 +329,7 @@ proc murmurHash(x: openArray[byte]): Hash =
|
||||
|
||||
# body
|
||||
while i < n * stepSize:
|
||||
var k1: uint32
|
||||
var k1: uint32 = uint32(0)
|
||||
|
||||
when nimvm:
|
||||
impl()
|
||||
@@ -348,7 +349,7 @@ proc murmurHash(x: openArray[byte]): Hash =
|
||||
h1 = h1*5 + n1
|
||||
|
||||
# tail
|
||||
var k1: uint32
|
||||
var k1: uint32 = uint32(0)
|
||||
var rem = size mod stepSize
|
||||
while rem > 0:
|
||||
dec rem
|
||||
@@ -396,13 +397,17 @@ proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
when nimvm: result = load4e(s, o)
|
||||
else:
|
||||
when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
|
||||
when declared copyMem:
|
||||
result = uint32(0)
|
||||
copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load4e(s, o)
|
||||
|
||||
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
when nimvm: result = load8e(s, o)
|
||||
else:
|
||||
when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
|
||||
when declared copyMem:
|
||||
result = uint64(0)
|
||||
copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load8e(s, o)
|
||||
|
||||
proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
|
||||
@@ -488,9 +493,9 @@ proc hashFarm(s: openArray[byte]): uint64 {.inline.} =
|
||||
x = seed
|
||||
y = seed*k1 + 113
|
||||
z = shiftMix(y*k2 + 113)*k2
|
||||
v, w: Pair
|
||||
v, w: Pair = default(Pair)
|
||||
x = x*k2 + load8(s)
|
||||
let eos = ((s.len - 1) div 64)*64
|
||||
let eos = ((s.len - 1) div 64)*64
|
||||
let last64 = eos + ((s.len - 1) and 63) - 63
|
||||
while true:
|
||||
x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1
|
||||
|
||||
@@ -74,6 +74,8 @@ proc delete[T](s: var seq[T], attr: T): bool =
|
||||
s[idx] = s[L-1]
|
||||
setLen(s, L-1)
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "",
|
||||
isLeaf = false): NimNode =
|
||||
|
||||
@@ -1961,6 +1961,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode,
|
||||
result.addNode(parse(x, errors))
|
||||
|
||||
proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
||||
result = nil
|
||||
case x.kind
|
||||
of xmlComment:
|
||||
result = newComment(x.rawData)
|
||||
@@ -2017,7 +2018,7 @@ proc parseHtml*(s: Stream, filename: string,
|
||||
errors: var seq[string]): XmlNode =
|
||||
## Parses the XML from stream `s` and returns a `XmlNode`. Every
|
||||
## occurred parsing error is added to the `errors` sequence.
|
||||
var x: XmlParser
|
||||
var x: XmlParser = default(XmlParser)
|
||||
open(x, s, filename, {reportComments, reportWhitespace, allowUnquotedAttribs,
|
||||
allowEmptyAttribs})
|
||||
next(x)
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
## validated to the server.
|
||||
##
|
||||
## ```Nim
|
||||
## import std/[httpclient]
|
||||
## var client = newHttpClient()
|
||||
## var data = newMultipartData()
|
||||
## data["output"] = "soap12"
|
||||
@@ -79,6 +80,7 @@
|
||||
## it, you can pass your own via the `mimeDb` parameter to avoid this.
|
||||
##
|
||||
## ```Nim
|
||||
## import std/[httpclient, mimetypes]
|
||||
## let mimes = newMimetypes()
|
||||
## var client = newHttpClient()
|
||||
## var data = newMultipartData()
|
||||
@@ -160,7 +162,7 @@
|
||||
## Example of setting SSL verification parameters in a new client:
|
||||
##
|
||||
## ```Nim
|
||||
## import httpclient
|
||||
## import std/[net, httpclient]
|
||||
## var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer))
|
||||
## ```
|
||||
##
|
||||
@@ -401,6 +403,7 @@ proc `$`*(data: MultipartData): string {.since: (1, 1).} =
|
||||
## convert MultipartData to string so it's human readable when echo
|
||||
## see https://github.com/nim-lang/Nim/issues/11863
|
||||
const sep = "-".repeat(30)
|
||||
result = ""
|
||||
for pos, entry in data.content:
|
||||
result.add(sep & center($pos, 3) & sep)
|
||||
result.add("\nname=\"" & entry.name & "\"")
|
||||
@@ -474,7 +477,7 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]],
|
||||
## data.addFiles({"uploaded_file": "public/test.html"})
|
||||
## ```
|
||||
for name, file in xs.items:
|
||||
var contentType: string
|
||||
var contentType: string = ""
|
||||
let (_, fName, ext) = splitFile(file)
|
||||
if ext.len > 0:
|
||||
contentType = mimeDb.getMimetype(ext[1..ext.high], "")
|
||||
@@ -862,7 +865,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
|
||||
while true:
|
||||
linei = 0
|
||||
when client is HttpClient:
|
||||
line = await client.socket.recvLine(client.timeout)
|
||||
line = client.socket.recvLine(client.timeout)
|
||||
else:
|
||||
line = await client.socket.recvLine()
|
||||
if line == "":
|
||||
@@ -968,7 +971,7 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
|
||||
else: nativesockets.Port(connectionUrl.port.parseInt)
|
||||
|
||||
when client is HttpClient:
|
||||
client.socket = await net.dial(connectionUrl.hostname, port)
|
||||
client.socket = net.dial(connectionUrl.hostname, port)
|
||||
elif client is AsyncHttpClient:
|
||||
client.socket = await asyncnet.dial(connectionUrl.hostname, port)
|
||||
else: {.fatal: "Unsupported client type".}
|
||||
@@ -1032,12 +1035,13 @@ proc format(entry: MultipartEntry, boundary: string): string =
|
||||
|
||||
proc format(client: HttpClient | AsyncHttpClient,
|
||||
multipart: MultipartData): Future[seq[string]] {.multisync.} =
|
||||
result = @[]
|
||||
let bound = getBoundary(multipart)
|
||||
client.headers["Content-Type"] = "multipart/form-data; boundary=" & bound
|
||||
|
||||
await client.readFileSizes(multipart)
|
||||
|
||||
var length: int64
|
||||
var length: int64 = int64(0)
|
||||
for entry in multipart.content:
|
||||
result.add(format(entry, bound) & httpNewLine)
|
||||
if entry.isFile:
|
||||
@@ -1080,7 +1084,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri,
|
||||
|
||||
var newHeaders: HttpHeaders
|
||||
|
||||
var data: seq[string]
|
||||
var data: seq[string] = @[]
|
||||
if multipart != nil and multipart.content.len > 0:
|
||||
# `format` modifies `client.headers`, see
|
||||
# https://github.com/nim-lang/Nim/pull/18208#discussion_r647036979
|
||||
@@ -1103,7 +1107,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri,
|
||||
await client.socket.send(headerString)
|
||||
|
||||
if data.len > 0:
|
||||
var buffer: string
|
||||
var buffer: string = ""
|
||||
for i, entry in multipart.content:
|
||||
buffer.add data[i]
|
||||
if not entry.isFile: continue
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user