mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 03:13:41 +00:00
Compare commits
333 Commits
pr_union_c
...
pr_fold_un
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e24a6fccb5 | ||
|
|
e7f48cdd5c | ||
|
|
1a901bd94e | ||
|
|
4aaabbb5b8 | ||
|
|
6112c51e78 | ||
|
|
2df633180a | ||
|
|
555191a3f0 | ||
|
|
96043bdbb7 | ||
|
|
af3181e75b | ||
|
|
652edb229a | ||
|
|
08c2a1741d | ||
|
|
33e455c986 | ||
|
|
9fcc3b0599 | ||
|
|
a788bae318 | ||
|
|
3eddb64909 | ||
|
|
b7b1003e6a | ||
|
|
e28d2f42e9 | ||
|
|
a610f23060 | ||
|
|
d8391a5c49 | ||
|
|
a2031ec6cf | ||
|
|
712f5be7eb | ||
|
|
f053767132 | ||
|
|
05c74d6844 | ||
|
|
75b512bc6a | ||
|
|
e239968b80 | ||
|
|
cd9ce377f8 | ||
|
|
371f50f66d | ||
|
|
cc696f18c0 | ||
|
|
726195d784 | ||
|
|
21420d8b09 | ||
|
|
1863f6447f | ||
|
|
ccb72c784d | ||
|
|
511ab72342 | ||
|
|
45e21ce8f1 | ||
|
|
76c5f16ac5 | ||
|
|
9d61f2cdd1 | ||
|
|
1fddb61b3b | ||
|
|
3e47725c08 | ||
|
|
8091d76306 | ||
|
|
b3c1fbaf13 | ||
|
|
45b8434c7d | ||
|
|
67ad1ae159 | ||
|
|
cfd8f8b857 | ||
|
|
c71de10608 | ||
|
|
f4b9fcc8ac | ||
|
|
0bc3f5c74c | ||
|
|
f98964d99f | ||
|
|
f5d80ede80 | ||
|
|
5f056f87b2 | ||
|
|
46bb47a444 | ||
|
|
08b82c90f5 | ||
|
|
8b88b5fdd8 | ||
|
|
7b47987341 | ||
|
|
c8072b1eb2 | ||
|
|
0e3330ee96 | ||
|
|
d55cd40642 | ||
|
|
658c9da33e | ||
|
|
5e56f0a356 | ||
|
|
74df699ff1 | ||
|
|
1576563775 | ||
|
|
4091576ab7 | ||
|
|
d61897459d | ||
|
|
815bbf0e73 | ||
|
|
3fc87259bd | ||
|
|
15271dba2f | ||
|
|
2e9422df57 | ||
|
|
24aa92c14f | ||
|
|
031ad957ba | ||
|
|
506c8a5ce8 | ||
|
|
40fc2d0e76 | ||
|
|
efd603eb28 | ||
|
|
79ce3fe6b7 | ||
|
|
aa90d00caf | ||
|
|
2af602a5c8 | ||
|
|
d303c289fa | ||
|
|
9d08f6cee3 | ||
|
|
294b1566e7 | ||
|
|
820e2bee9c | ||
|
|
2864830941 | ||
|
|
dd3a4b2aba | ||
|
|
b534f34e95 | ||
|
|
3aaaed1acf | ||
|
|
40b37c96d2 | ||
|
|
baf3695c76 | ||
|
|
2c56872b5c | ||
|
|
ca5df9ab25 | ||
|
|
67442471ae | ||
|
|
e69eb99a15 | ||
|
|
041098e882 | ||
|
|
93c24fe1c5 | ||
|
|
ae9287c4f3 | ||
|
|
0a058a6b8f | ||
|
|
0806fb0b6f | ||
|
|
68b2e9eb6a | ||
|
|
fce86e5937 | ||
|
|
5fa96ef270 | ||
|
|
b8f6088ac0 | ||
|
|
52cf7dfde0 | ||
|
|
0347536ff2 | ||
|
|
d0b6b9346e | ||
|
|
8be82c36c9 | ||
|
|
4a056b1849 | ||
|
|
a3aea224c9 | ||
|
|
53460f312c | ||
|
|
922f7dfd71 | ||
|
|
3e8f44b232 | ||
|
|
8b39b2df7d | ||
|
|
6df050d6d2 | ||
|
|
34c87de984 | ||
|
|
d4b9c147ab | ||
|
|
07628b0dec | ||
|
|
80e6b35721 | ||
|
|
71515bf278 | ||
|
|
a0d78d259b | ||
|
|
1dbf614858 | ||
|
|
2f7586c066 | ||
|
|
720d0aee5c | ||
|
|
def1fea43a | ||
|
|
25c068c070 | ||
|
|
1bebc236bd | ||
|
|
449106a5a4 | ||
|
|
bb0006598d | ||
|
|
f5cb39289b | ||
|
|
af23bc2941 | ||
|
|
aaf6c408c6 | ||
|
|
706985997e | ||
|
|
9c85f4fd07 | ||
|
|
274762638f | ||
|
|
e9a4d096ab | ||
|
|
2f904535d0 | ||
|
|
96d6eee9bc | ||
|
|
67ea754b7f | ||
|
|
95a7695810 | ||
|
|
f73e03b132 | ||
|
|
d72b848d17 | ||
|
|
d6633ae1da | ||
|
|
4515b2dae2 | ||
|
|
30e552e3d3 | ||
|
|
c73eedfe6e | ||
|
|
911cef1621 | ||
|
|
ea9811a4d2 | ||
|
|
7f2e6a1359 | ||
|
|
a2ee709199 | ||
|
|
a65501325c | ||
|
|
cad8726907 | ||
|
|
aa605da92a | ||
|
|
09043f409f | ||
|
|
9e30b39412 | ||
|
|
4a63186cda | ||
|
|
782b75cc08 | ||
|
|
f420a5a273 | ||
|
|
7dfadb8b4e | ||
|
|
4eed341ba5 | ||
|
|
d98ef312f0 | ||
|
|
89978b48ba | ||
|
|
d6a71a1067 | ||
|
|
f7cb0322c2 | ||
|
|
78983f1876 | ||
|
|
0e1df88f7e | ||
|
|
a5f46a72ba | ||
|
|
4974baf7fa | ||
|
|
b82ff5a87b | ||
|
|
2a48182288 | ||
|
|
febc58e036 | ||
|
|
b0e6d28782 | ||
|
|
7974a2208c | ||
|
|
7cbe031909 | ||
|
|
4f5c0efaf2 | ||
|
|
821d0806fe | ||
|
|
2cdc0e913f | ||
|
|
dc3ffb6a71 | ||
|
|
56a3dd57fb | ||
|
|
7cccf36d7b | ||
|
|
d4027f25c4 | ||
|
|
75b9d66582 | ||
|
|
c21bf7f41b | ||
|
|
62a5bb4d0a | ||
|
|
fd379c2f94 | ||
|
|
1bd5a4a99e | ||
|
|
a27542195c | ||
|
|
69b2a6effc | ||
|
|
6d6489a9ab | ||
|
|
3b85c1a2e9 | ||
|
|
b9de2bb4f3 | ||
|
|
6f6e34ebb0 | ||
|
|
a55c15c651 | ||
|
|
7da2ffb751 | ||
|
|
5c843d3d60 | ||
|
|
a1777200c1 | ||
|
|
d51d88700b | ||
|
|
37dba853c9 | ||
|
|
755307be61 | ||
|
|
05a7a48a2b | ||
|
|
84f5060e94 | ||
|
|
ff005ad7dc | ||
|
|
6cc50ec316 | ||
|
|
58cf62451d | ||
|
|
00ac961ab1 | ||
|
|
0c3573e4a0 | ||
|
|
79b17b7c05 | ||
|
|
1660ddf98a | ||
|
|
c759d7abd1 | ||
|
|
04ccd2f4f0 | ||
|
|
680a13a142 | ||
|
|
21a161a535 | ||
|
|
1fbb67ffe9 | ||
|
|
b5f2eafed1 | ||
|
|
fe55dcb2be | ||
|
|
651fdbe586 | ||
|
|
d0dc4ac22f | ||
|
|
22d2cf2175 | ||
|
|
ecc6a1d92b | ||
|
|
6c35a36043 | ||
|
|
4ff35585f8 | ||
|
|
6d362e0ffe | ||
|
|
61e04ba0ed | ||
|
|
793cee4de1 | ||
|
|
9dda7ff7bc | ||
|
|
771369237c | ||
|
|
baec1955b5 | ||
|
|
21771765a2 | ||
|
|
3a55bae53f | ||
|
|
fcee829d85 | ||
|
|
8b895afcb5 | ||
|
|
09682ac7f1 | ||
|
|
a6595e5b49 | ||
|
|
9ff0333a4c | ||
|
|
24e5b21c90 | ||
|
|
f223f016f3 | ||
|
|
7de4ace949 | ||
|
|
6a5aa00701 | ||
|
|
79a65da22a | ||
|
|
29a7d60acb | ||
|
|
ca28c256f3 | ||
|
|
ebcfd96ae1 | ||
|
|
cd22560af5 | ||
|
|
7cd1777218 | ||
|
|
a93c5d79b9 | ||
|
|
c10f84b9d7 | ||
|
|
4a548deb08 | ||
|
|
d77ea07837 | ||
|
|
bf865fa75a | ||
|
|
d91297a330 | ||
|
|
9ff15da426 | ||
|
|
e265b3dfdd | ||
|
|
99f4cfa438 | ||
|
|
6908fb4011 | ||
|
|
080b0a03bd | ||
|
|
c314155fb3 | ||
|
|
f69809bb17 | ||
|
|
c948ab9b85 | ||
|
|
4bf323d6c4 | ||
|
|
538603e01d | ||
|
|
1ebdcb3bca | ||
|
|
d27061f6da | ||
|
|
71de7fca9e | ||
|
|
c8af0996fd | ||
|
|
5e55e16ad8 | ||
|
|
4789af71fe | ||
|
|
fc853cb726 | ||
|
|
11ead19bc1 | ||
|
|
aa0d8e9cfe | ||
|
|
b7b1313d21 | ||
|
|
d7e77b330f | ||
|
|
da7670c8e0 | ||
|
|
1244ffbf39 | ||
|
|
5e8cd318ef | ||
|
|
770f8d5513 | ||
|
|
d3af51e3ce | ||
|
|
ea7c2a4409 | ||
|
|
d53a9cf288 | ||
|
|
540b414c86 | ||
|
|
f09c549d42 | ||
|
|
7e88091de3 | ||
|
|
4c250d69a8 | ||
|
|
69ea1336fb | ||
|
|
09dcff71c8 | ||
|
|
0d53b6e027 | ||
|
|
4ef06a5cc5 | ||
|
|
446501b53b | ||
|
|
cb7bcae7f7 | ||
|
|
79f5a74408 | ||
|
|
04da0a6028 | ||
|
|
ac0179ced9 | ||
|
|
2311049b27 | ||
|
|
832ba815d1 | ||
|
|
12b90d7c07 | ||
|
|
e38cbd3c84 | ||
|
|
ab18962085 | ||
|
|
6320b0cd5b | ||
|
|
eed9cb0d3f | ||
|
|
1befb8d4a3 | ||
|
|
6336d2681b | ||
|
|
dda638c1ba | ||
|
|
43274bfb92 | ||
|
|
26107e931c | ||
|
|
34719cad9d | ||
|
|
8bd0422767 | ||
|
|
58813a3b2e | ||
|
|
a4dff1a03e | ||
|
|
2e4d344b43 | ||
|
|
f7c11a8978 | ||
|
|
a354b18fe1 | ||
|
|
0ffc0493a3 | ||
|
|
253fafb305 | ||
|
|
e96fad1eed | ||
|
|
995081b56a | ||
|
|
d43a5954c5 | ||
|
|
298ada3412 | ||
|
|
06b25bd2c4 | ||
|
|
2a046e6487 | ||
|
|
a33e2b76ae | ||
|
|
ddc47fecca | ||
|
|
c5b206d4ac | ||
|
|
630c304a2d | ||
|
|
0c890ff9a7 | ||
|
|
7a0069a134 | ||
|
|
20043ea09e | ||
|
|
b215ec3735 | ||
|
|
e0e698be9a | ||
|
|
fbf9e94145 | ||
|
|
b9b24e192a | ||
|
|
a64aa51fe9 | ||
|
|
f0e1eef65e | ||
|
|
4954469259 | ||
|
|
1d59e1cbb6 | ||
|
|
2a2474d395 | ||
|
|
d164f87fbc | ||
|
|
c0aa951ee0 | ||
|
|
6126a0bf46 | ||
|
|
d76ea4f323 | ||
|
|
c97a20ce49 | ||
|
|
12b9680291 |
35
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
35
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -7,17 +7,16 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- **Please provide a minimal code example that reproduces the Bug!** :bug:
|
||||
Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
|
||||
Please provide a minimal code example that reproduces the bug if possible.
|
||||
Reports with a reproducible example or detailed information will likely receive fixes faster.
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Use DETAILED DESCRIPTIVE information about the problem.
|
||||
Here, you go into more details about your Bug report. This section can be a few paragraphs long.
|
||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
||||
Describe the problem. Code example can be given here.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -25,7 +24,9 @@ body:
|
||||
id: nim-version
|
||||
attributes:
|
||||
label: Nim Version
|
||||
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
|
||||
description: |
|
||||
Can be obtained from `nim -v` on the command line along with the OS/architecture.
|
||||
For development versions, make sure to include the commit hash.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -34,7 +35,7 @@ body:
|
||||
attributes:
|
||||
label: Current Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
@@ -42,14 +43,14 @@ body:
|
||||
attributes:
|
||||
label: Expected Output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
render: text
|
||||
|
||||
- type: textarea
|
||||
id: possible-solution
|
||||
id: known-workarounds
|
||||
attributes:
|
||||
label: Possible Solution
|
||||
description: Propose a possible solution.
|
||||
label: Known Workarounds
|
||||
description: Provide any known workarounds.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
@@ -64,13 +65,9 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
- Thanks for your contributions!, your Bug report will receive feedback from the community soon...
|
||||
- Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
||||
- Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
|
||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
||||
- If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
|
||||
which should give more context on a compiler crash.
|
||||
- If it's a regression, you can help us by identifying which version introduced the bug,
|
||||
see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
|
||||
or at least try known past releases (e.g. `choosenim 2.0.0`). The Nim repo also supports online bisecting
|
||||
via making a comment, which contains a code block starting by `!nim c`, `!nim js` etc. , see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
|
||||
- If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
|
||||
- If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
|
||||
The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
|
||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
||||
|
||||
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-12
|
||||
os: macos-13
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
4
.github/workflows/ci_packages.yml
vendored
4
.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-12]
|
||||
os: [ubuntu-20.04, macos-13]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
@@ -48,6 +49,7 @@ jobs:
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
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:
|
||||
@@ -29,10 +29,10 @@ jobs:
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-12'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-12'
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
@@ -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
|
||||
|
||||
@@ -130,6 +133,7 @@ jobs:
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
||||
# XXX can't find boehm on macos 13
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
@@ -24,6 +24,6 @@ if not exist %nim_csources% (
|
||||
cd ..
|
||||
copy /y bin\nim.exe %nim_csources%
|
||||
)
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
|
||||
135
changelog.md
135
changelog.md
@@ -1,120 +1,69 @@
|
||||
# v2.2.0 - yyyy-mm-dd
|
||||
# v2.x.x - yyyy-mm-dd
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to be in the same module where their type has been defined.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
- `internalNew` is removed from system, use `new` instead.
|
||||
- `-d:nimPreviewFloatRoundtrip` becomes the default. `system.addFloat` and `system.$` now can produce string representations of
|
||||
floating point numbers that are minimal in size and possess round-trip and correct
|
||||
rounding guarantees (via the
|
||||
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm). Use `-d:nimLegacySprintf` to emulate old behaviors.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
- The `default` parameter of `tables.getOrDefault` has been renamed to `def` to
|
||||
avoid conflicts with `system.default`, so named argument usage for this
|
||||
parameter like `getOrDefault(..., default = ...)` will have to be changed.
|
||||
|
||||
- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
- With `-d:nimPreviewCheckedClose`, the `close` function in the `std/syncio` module now raises an IO exception in case of an error.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
|
||||
[//]: # "Additions:"
|
||||
- `setutils.symmetricDifference` along with its operator version
|
||||
`` setutils.`-+-` `` and in-place version `setutils.toggle` have been added
|
||||
to more efficiently calculate the symmetric difference of bitsets.
|
||||
|
||||
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to system, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
[//]: # "Changes:"
|
||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.htm#constructor-initializer
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
- C++ `constructor` now reuses `result` instead creating `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- An experimental option `genericsOpenSym` has been added to allow captured
|
||||
symbols in generic routine bodies to be replaced by symbols injected locally
|
||||
by templates/macros at instantiation time. `bind` may be used to keep the
|
||||
captured symbols over the injected ones regardless of enabling the option.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`genericsOpenSym` needs to be enabled, and a warning is given in the case
|
||||
where an injected symbol would replace a captured symbol not bound by `bind`
|
||||
and the experimental switch isn't enabled.
|
||||
- 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
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped) =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
# objs.nim
|
||||
import std/hashes
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
|
||||
echo old[int]() # "captured"
|
||||
type
|
||||
Obj* = object
|
||||
x*, y*: int
|
||||
z*: string # to be ignored for equality
|
||||
|
||||
{.experimental: "genericsOpenSym".}
|
||||
proc `==`*(a, b: Obj): bool =
|
||||
a.x == b.x and a.y == b.y
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
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
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
|
||||
@@ -1,12 +1,248 @@
|
||||
# v2.2.0 - 2023-mm-dd
|
||||
# v2.2.0 - 2024-10-02
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
|
||||
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
|
||||
- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
|
||||
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
|
||||
```nim
|
||||
var a, b: int
|
||||
(a, b) = (1, 2, 3, 4)
|
||||
```
|
||||
will no longer compile.
|
||||
|
||||
- `internalNew` is removed from the `system` module, use `new` instead.
|
||||
|
||||
- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
|
||||
|
||||
- The JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
|
||||
|
||||
- The JS backend now supports closure iterators.
|
||||
|
||||
- `owner` in `std/macros` is deprecated.
|
||||
|
||||
- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
|
||||
Previously; in templates they would error immediately at the template definition,
|
||||
and in generic procs a type symbol would arbitrarily be captured, losing the
|
||||
information of the other symbols. This means that generic code can now give
|
||||
errors for ambiguous type symbols, and macros operating on generic proc AST
|
||||
may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
|
||||
|
||||
- Partial generic instantiation of routines is no longer allowed. Previously
|
||||
it compiled in niche situations due to bugs in the compiler.
|
||||
|
||||
```nim
|
||||
proc foo[T, U](x: T, y: U) = echo (x, y)
|
||||
proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
|
||||
|
||||
proc bar[T]() =
|
||||
foo[float](1, "abc")
|
||||
|
||||
bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
|
||||
```
|
||||
|
||||
- `const` values now open a new scope for each constant, meaning symbols
|
||||
declared in them can no longer be used outside or in the value of
|
||||
other constants.
|
||||
|
||||
```nim
|
||||
const foo = (var a = 1; a)
|
||||
const bar = a # error
|
||||
let baz = a # error
|
||||
```
|
||||
|
||||
- The following POSIX wrappers have had their types changed from signed to
|
||||
unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
|
||||
- `Gid` (was `int32`, is now `uint32`)
|
||||
- `Uid` (was `int32`, is now `uint32`)
|
||||
- `Dev` (was `int32`, is now `uint32` on FreeBSD)
|
||||
- `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
|
||||
- `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
|
||||
(were `int32`, are now `uint32`)
|
||||
- `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
|
||||
|
||||
- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
|
||||
`std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
|
||||
- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
|
||||
- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to the `system` module, which doesn't initialize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
|
||||
convert a value with a range type to its base type.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
|
||||
on Google's Farm Hash) which is also often faster than the present one. Define
|
||||
`nimStringHash2` to get the old values back. `--jsbigint=off` mode always only
|
||||
produces the old values. This may impact your automated tests if they depend
|
||||
on hash order in some obvious or indirect way. Using `sorted` or `OrderedTable`
|
||||
is often an easy workaround.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
|
||||
|
||||
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
|
||||
|
||||
- `member` can be used to attach a procedure to a C++ type.
|
||||
|
||||
- Inside a C++ constructor, `result` can be used to access the created object rather than `this`.
|
||||
|
||||
- Tuple unpacking changes:
|
||||
- Tuple unpacking assignment now supports using underscores to discard values.
|
||||
```nim
|
||||
var a, c: int
|
||||
(a, _, c) = (1, 2, 3)
|
||||
```
|
||||
- Tuple unpacking variable declarations now support type annotations, but
|
||||
only for the entire tuple.
|
||||
```nim
|
||||
let (a, b): (int, int) = (1, 2)
|
||||
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
|
||||
```
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# v1.xx.x - yyyy-mm-dd
|
||||
# v2.xx.x - yyyy-mm-dd
|
||||
|
||||
This is an example file.
|
||||
The changes should go to changelog.md!
|
||||
|
||||
@@ -214,7 +214,8 @@ type
|
||||
tyUncheckedArray
|
||||
# An array with boundaries [0,+∞]
|
||||
|
||||
tyProxy # used as errornous type (for idetools)
|
||||
tyError # used as erroneous type (for idetools)
|
||||
# as an erroneous node should match everything
|
||||
|
||||
tyBuiltInTypeClass
|
||||
# Type such as the catch-all object, tuple, seq, etc
|
||||
@@ -276,13 +277,9 @@ static:
|
||||
const
|
||||
tyPureObject* = tyTuple
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
tyError* = tyProxy # as an errornous node should match everything
|
||||
tyUnknown* = tyFromExpr
|
||||
|
||||
tyUnknownTypes* = {tyError, tyFromExpr}
|
||||
|
||||
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyConcept,
|
||||
tyAnd, tyOr, tyNot, tyAnything}
|
||||
|
||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses
|
||||
@@ -325,9 +322,9 @@ type
|
||||
nfFirstWrite # this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
nfOpenSym # node is a captured sym but can be overriden by local symbols
|
||||
# ideally a unary node containing nkSym/nkOpenSymChoice or an
|
||||
# extension over nkOpenSymChoice
|
||||
nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
|
||||
# because openSym experimental switch is disabled
|
||||
# gives warning instead
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
|
||||
@@ -448,6 +445,10 @@ const
|
||||
tfGcSafe* = tfThread
|
||||
tfObjHasKids* = tfEnumHasHoles
|
||||
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
|
||||
@@ -492,7 +493,7 @@ type
|
||||
mAnd, mOr,
|
||||
mImplies, mIff, mExists, mForall, mOld,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
|
||||
mConStrStr, mSlice,
|
||||
mDotDot, # this one is only necessary to give nice compile time warnings
|
||||
mFields, mFieldPairs, mOmpParFor,
|
||||
@@ -560,7 +561,7 @@ const
|
||||
mStrToStr, mEnumToStr,
|
||||
mAnd, mOr,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mXorSet,
|
||||
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mOpenArrayToSeq}
|
||||
|
||||
@@ -592,7 +593,7 @@ type
|
||||
TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes
|
||||
when defined(useNodeIds):
|
||||
id*: int
|
||||
typ*: PType
|
||||
typField: PType
|
||||
info*: TLineInfo
|
||||
flags*: TNodeFlags
|
||||
case kind*: TNodeKind
|
||||
@@ -707,7 +708,7 @@ type
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
|
||||
owner*: PSym
|
||||
ownerField: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
# the whole proc including header; this is used
|
||||
@@ -776,7 +777,7 @@ type
|
||||
# formal param list
|
||||
# for concepts, the concept body
|
||||
# else: unused
|
||||
owner*: PSym # the 'owner' of the type
|
||||
ownerField: PSym # the 'owner' of the type
|
||||
sym*: PSym # types have the sym associated with them
|
||||
# it is used for converting types to strings
|
||||
size*: BiggestInt # the size of the type in bytes
|
||||
@@ -815,6 +816,15 @@ type
|
||||
|
||||
template nodeId(n: PNode): int = cast[int](n)
|
||||
|
||||
template typ*(n: PNode): PType =
|
||||
n.typField
|
||||
|
||||
proc owner*(s: PSym|PType): PSym {.inline.} =
|
||||
result = s.ownerField
|
||||
|
||||
proc setOwner*(s: PSym|PType, owner: PSym) {.inline.} =
|
||||
s.ownerField = owner
|
||||
|
||||
type Gconfig = object
|
||||
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
|
||||
# reduces memory usage given that `PNode` is the most allocated type by far.
|
||||
@@ -882,7 +892,7 @@ const
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfOpenSym}
|
||||
nfDisabledOpenSym}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -896,7 +906,7 @@ const
|
||||
nfAllFieldsSet* = nfBase2
|
||||
|
||||
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
|
||||
nkClosedSymChoice}
|
||||
nkClosedSymChoice, nkOpenSym}
|
||||
|
||||
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
|
||||
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||
@@ -924,6 +934,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
of nkOpenSym: getPIdent(a.sons[0])
|
||||
else: nil
|
||||
|
||||
const
|
||||
@@ -1057,8 +1068,11 @@ proc getDeclPragma*(n: PNode): PNode =
|
||||
|
||||
proc extractPragma*(s: PSym): PNode =
|
||||
## gets the pragma node of routine/type/var/let/const symbol `s`
|
||||
if s.kind in routineKinds:
|
||||
result = s.ast[pragmasPos]
|
||||
if s.kind in routineKinds: # bug #24167
|
||||
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
|
||||
result = s.ast[pragmasPos]
|
||||
else:
|
||||
result = nil
|
||||
elif s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.ast != nil and s.ast.len > 0:
|
||||
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
|
||||
@@ -1129,7 +1143,7 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||
## new node with line info, type, and no children
|
||||
result = newNode(kind)
|
||||
result.info = info
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
|
||||
## new node with line info, no type, and no children
|
||||
@@ -1194,7 +1208,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
|
||||
assert not name.isNil
|
||||
let id = nextSymId idgen
|
||||
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
||||
options: options, owner: owner, offset: defaultOffset,
|
||||
options: options, ownerField: owner, offset: defaultOffset,
|
||||
disamb: getOrDefault(idgen.disambTable, name).int32)
|
||||
idgen.disambTable.inc name
|
||||
when false:
|
||||
@@ -1275,15 +1289,18 @@ proc newIdentNode*(ident: PIdent, info: TLineInfo): PNode =
|
||||
proc newSymNode*(sym: PSym): PNode =
|
||||
result = newNode(nkSym)
|
||||
result.sym = sym
|
||||
result.typ = sym.typ
|
||||
result.typ() = sym.typ
|
||||
result.info = sym.info
|
||||
|
||||
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result = newNode(nkSym)
|
||||
result.sym = sym
|
||||
result.typ = sym.typ
|
||||
result.typ() = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newOpenSym*(n: PNode): PNode {.inline.} =
|
||||
result = newTreeI(nkOpenSym, n.info, n)
|
||||
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = intVal
|
||||
@@ -1358,7 +1375,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
||||
result = newNode(nkIntLit)
|
||||
else: raiseAssert $kind
|
||||
result.intVal = intVal
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
|
||||
# XXX: introduce range check
|
||||
@@ -1497,7 +1514,7 @@ iterator signature*(t: PType): PType =
|
||||
|
||||
proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
|
||||
let id = nextTypeId idgen
|
||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||
result = PType(kind: kind, ownerField: owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: id,
|
||||
uniqueId: id, sons: @[])
|
||||
if son != nil: result.sons.add son
|
||||
@@ -1508,6 +1525,7 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType
|
||||
|
||||
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
|
||||
proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
|
||||
proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.k == low(typeof(a.k)): a.k = b.k
|
||||
@@ -1551,7 +1569,7 @@ proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
|
||||
result.sym = t.sym # backend-info should not be copied
|
||||
|
||||
proc exactReplica*(t: PType): PType =
|
||||
result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
|
||||
result = PType(kind: t.kind, ownerField: t.owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: t.itemId,
|
||||
uniqueId: t.uniqueId)
|
||||
assignType(result, t)
|
||||
@@ -1629,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
|
||||
|
||||
@@ -1659,7 +1677,7 @@ proc copyNode*(src: PNode): PNode =
|
||||
return nil
|
||||
result = newNode(src.kind)
|
||||
result.info = src.info
|
||||
result.typ = src.typ
|
||||
result.typ() = src.typ
|
||||
result.flags = src.flags * PersistentNodeFlags
|
||||
result.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
@@ -1677,7 +1695,7 @@ proc copyNode*(src: PNode): PNode =
|
||||
|
||||
template transitionNodeKindCommon(k: TNodeKind) =
|
||||
let obj {.inject.} = n[]
|
||||
n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags)
|
||||
n[] = TNode(kind: k, typField: n.typ, info: obj.info, flags: obj.flags)
|
||||
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
|
||||
when defined(useNodeIds):
|
||||
n.id = obj.id
|
||||
@@ -1700,7 +1718,7 @@ proc transitionNoneToSym*(n: PNode) =
|
||||
template transitionSymKindCommon*(k: TSymKind) =
|
||||
let obj {.inject.} = s[]
|
||||
s[] = TSym(kind: k, itemId: obj.itemId, magic: obj.magic, typ: obj.typ, name: obj.name,
|
||||
info: obj.info, owner: obj.owner, flags: obj.flags, ast: obj.ast,
|
||||
info: obj.info, ownerField: obj.ownerField, flags: obj.flags, ast: obj.ast,
|
||||
options: obj.options, position: obj.position, offset: obj.offset,
|
||||
loc: obj.loc, annex: obj.annex, constraint: obj.constraint)
|
||||
when hasFFI:
|
||||
@@ -1728,7 +1746,7 @@ template copyNodeImpl(dst, src, processSonsStmt) =
|
||||
dst.info = src.info
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
dst.typ = src.typ
|
||||
dst.typ() = src.typ
|
||||
dst.flags = src.flags * PersistentNodeFlags
|
||||
dst.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import pragmas, options, ast, trees
|
||||
|
||||
proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
||||
optionsStack.add options
|
||||
|
||||
proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
||||
options = optionsStack[^1]
|
||||
optionsStack.setLen(optionsStack.len-1)
|
||||
|
||||
proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions,
|
||||
n: PNode, start: int) =
|
||||
pushBackendOption(optionsStack, options)
|
||||
for i in start..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit:
|
||||
let sw = whichPragma(it[0])
|
||||
let opts = pragmaToOptions(sw)
|
||||
if opts != {}:
|
||||
if it[1].intVal != 0:
|
||||
options.incl opts
|
||||
else:
|
||||
options.excl opts
|
||||
|
||||
template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) =
|
||||
popBackendOption(optionsStack, options)
|
||||
164
compiler/cbuilderbase.nim
Normal file
164
compiler/cbuilderbase.nim
Normal file
@@ -0,0 +1,164 @@
|
||||
import ropes, int128
|
||||
|
||||
type
|
||||
Snippet* = string
|
||||
Builder* = object
|
||||
buf*: string
|
||||
indents*: int
|
||||
|
||||
template newBuilder*(s: string): Builder =
|
||||
Builder(buf: s)
|
||||
|
||||
proc extract*(builder: Builder): Snippet =
|
||||
builder.buf
|
||||
|
||||
proc add*(builder: var Builder, s: string) =
|
||||
builder.buf.add(s)
|
||||
|
||||
proc add*(builder: var Builder, s: char) =
|
||||
builder.buf.add(s)
|
||||
|
||||
proc addNewline*(builder: var Builder) =
|
||||
builder.add('\n')
|
||||
for i in 0 ..< builder.indents:
|
||||
builder.add('\t')
|
||||
|
||||
proc addLineEnd*(builder: var Builder, s: string) =
|
||||
builder.add(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addLineEndIndent*(builder: var Builder, s: string) =
|
||||
inc builder.indents
|
||||
builder.add(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addDedent*(builder: var Builder, s: string) =
|
||||
if builder.buf.len > 0 and builder.buf[^1] == '\t':
|
||||
builder.buf.setLen(builder.buf.len - 1)
|
||||
builder.add(s)
|
||||
dec builder.indents
|
||||
|
||||
proc addLineEndDedent*(builder: var Builder, s: string) =
|
||||
builder.addDedent(s)
|
||||
builder.addNewline()
|
||||
|
||||
proc addLineComment*(builder: var Builder, comment: string) =
|
||||
# probably no-op on nifc
|
||||
builder.add("// ")
|
||||
builder.add(comment)
|
||||
builder.addNewline()
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: int) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: int64) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: uint64) =
|
||||
builder.buf.addInt(val)
|
||||
|
||||
proc addIntValue*(builder: var Builder, val: Int128) =
|
||||
builder.buf.addInt128(val)
|
||||
|
||||
template cIntValue*(val: int): Snippet = $val
|
||||
template cIntValue*(val: int64): Snippet = $val
|
||||
template cIntValue*(val: uint64): Snippet = $val
|
||||
template cIntValue*(val: Int128): Snippet = $val
|
||||
|
||||
template cUintValue*(val: uint): Snippet = $val & "U"
|
||||
|
||||
import std/formatfloat
|
||||
|
||||
proc addFloatValue*(builder: var Builder, val: float) =
|
||||
builder.buf.addFloat(val)
|
||||
|
||||
template cFloatValue*(val: float): Snippet = $val
|
||||
|
||||
proc addInt64Literal*(result: var Builder; i: BiggestInt) =
|
||||
if i > low(int64):
|
||||
result.add "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc addUint64Literal*(result: var Builder; i: uint64) =
|
||||
result.add rope($i & "ULL")
|
||||
|
||||
proc addIntLiteral*(result: var Builder; i: BiggestInt) =
|
||||
if i > low(int32) and i <= high(int32):
|
||||
result.addIntValue(i)
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
result.add "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
result.add "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result.add "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc addIntLiteral*(result: var Builder; i: Int128) =
|
||||
addIntLiteral(result, toInt64(i))
|
||||
|
||||
proc cInt64Literal*(i: BiggestInt): Snippet =
|
||||
if i > low(int64):
|
||||
result = "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result = "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc cUint64Literal*(i: uint64): Snippet =
|
||||
result = $i & "ULL"
|
||||
|
||||
proc cIntLiteral*(i: BiggestInt): Snippet =
|
||||
if i > low(int32) and i <= high(int32):
|
||||
result = rope(i)
|
||||
elif i == low(int32):
|
||||
# Nim has the same bug for the same reasons :-)
|
||||
result = "(-2147483647 -1)"
|
||||
elif i > low(int64):
|
||||
result = "IL64($1)" % [rope(i)]
|
||||
else:
|
||||
result = "(IL64(-9223372036854775807) - IL64(1))"
|
||||
|
||||
proc cIntLiteral*(i: Int128): Snippet =
|
||||
result = cIntLiteral(toInt64(i))
|
||||
|
||||
const
|
||||
NimInt* = "NI"
|
||||
NimInt8* = "NI8"
|
||||
NimInt16* = "NI16"
|
||||
NimInt32* = "NI32"
|
||||
NimInt64* = "NI64"
|
||||
CInt* = "int"
|
||||
NimUint* = "NU"
|
||||
NimUint8* = "NU8"
|
||||
NimUint16* = "NU16"
|
||||
NimUint32* = "NU32"
|
||||
NimUint64* = "NU64"
|
||||
NimFloat* = "NF"
|
||||
NimFloat32* = "NF32"
|
||||
NimFloat64* = "NF64"
|
||||
NimFloat128* = "NF128" # not actually defined
|
||||
NimNan* = "NAN"
|
||||
NimInf* = "INF"
|
||||
NimBool* = "NIM_BOOL"
|
||||
NimTrue* = "NIM_TRUE"
|
||||
NimFalse* = "NIM_FALSE"
|
||||
NimChar* = "NIM_CHAR"
|
||||
CChar* = "char"
|
||||
NimCstring* = "NCSTRING"
|
||||
NimNil* = "NIM_NIL"
|
||||
CNil* = "NULL"
|
||||
NimStrlitFlag* = "NIM_STRLIT_FLAG"
|
||||
CVoid* = "void"
|
||||
CPointer* = "void*"
|
||||
CConstPointer* = "NIM_CONST void*"
|
||||
|
||||
proc cIntType*(bits: BiggestInt): Snippet =
|
||||
"NI" & $bits
|
||||
|
||||
proc cUintType*(bits: BiggestInt): Snippet =
|
||||
"NU" & $bits
|
||||
|
||||
type
|
||||
IfBuilderState* = enum
|
||||
WaitingIf, WaitingElseIf, InBlock
|
||||
IfBuilder* = object
|
||||
state*: IfBuilderState
|
||||
668
compiler/cbuilderdecls.nim
Normal file
668
compiler/cbuilderdecls.nim
Normal file
@@ -0,0 +1,668 @@
|
||||
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.addLineEnd(";")
|
||||
|
||||
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.addLineEnd(";")
|
||||
|
||||
template addVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string,
|
||||
typ: Snippet, initializerBody: typed) =
|
||||
## adds a variable declaration to the builder, with
|
||||
## `initializerBody` building the initializer. initializer must be provided
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
initializerBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
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.addLineEnd(";")
|
||||
|
||||
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.addIntValue(len)
|
||||
builder.add("]")
|
||||
if initializer.len != 0:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
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.addIntValue(len)
|
||||
builder.add("] = ")
|
||||
body
|
||||
builder.addLineEnd(";")
|
||||
|
||||
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.addLineEnd(";")
|
||||
|
||||
proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
builder.add("typedef ")
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, 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.addIntValue(len)
|
||||
builder.addLineEnd("];")
|
||||
|
||||
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.addIntValue(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.addIntValue(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.addIntValue(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
obj.add(CallingConvToStr[callConv])
|
||||
obj.add("_PTR(")
|
||||
obj.add(rettype)
|
||||
obj.add(", ")
|
||||
obj.add(name)
|
||||
obj.add(")")
|
||||
obj.add(params)
|
||||
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.buf.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.buf.len:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = CChar)
|
||||
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.buf.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 = CChar)
|
||||
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.buf.len and
|
||||
t.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = CChar)
|
||||
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:
|
||||
obj.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")
|
||||
|
||||
template addUnionType(obj: var Builder; body: typed) =
|
||||
## adds a union type i.e. `union { ... }` with fields according to `body`
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("}")
|
||||
|
||||
type DeclVisibility = enum
|
||||
None
|
||||
Extern
|
||||
ExternC
|
||||
ImportLib
|
||||
ExportLib
|
||||
ExportLibVar
|
||||
Private
|
||||
StaticProc
|
||||
|
||||
proc addVisibilityPrefix(builder: var Builder, visibility: DeclVisibility) =
|
||||
# internal proc
|
||||
case visibility
|
||||
of None: discard
|
||||
of Extern:
|
||||
builder.add("extern ")
|
||||
of ExternC:
|
||||
builder.add("NIM_EXTERNC ")
|
||||
of ImportLib:
|
||||
builder.add("N_LIB_IMPORT ")
|
||||
of ExportLib:
|
||||
builder.add("N_LIB_EXPORT ")
|
||||
of ExportLibVar:
|
||||
builder.add("N_LIB_EXPORT_VAR ")
|
||||
of Private:
|
||||
builder.add("N_LIB_PRIVATE ")
|
||||
of StaticProc:
|
||||
builder.add("static ")
|
||||
|
||||
template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
|
||||
## adds a declaration as in `declBody` with the given visibility
|
||||
builder.addVisibilityPrefix(visibility)
|
||||
declBody
|
||||
|
||||
type ProcParamBuilder = object
|
||||
needsComma: bool
|
||||
|
||||
proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
|
||||
result = ProcParamBuilder(needsComma: false)
|
||||
builder.add("(")
|
||||
|
||||
proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
|
||||
if params.needsComma:
|
||||
builder.add(")")
|
||||
else:
|
||||
builder.add("void)")
|
||||
|
||||
template cgDeclFrmt*(s: PSym): string =
|
||||
s.constraint.strVal
|
||||
|
||||
proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add(typ)
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
|
||||
proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
var modifiedTyp = typ
|
||||
if sfNoalias in param.flags:
|
||||
modifiedTyp.add(" NIM_NOALIAS")
|
||||
if sfCodegenDecl notin param.flags:
|
||||
builder.add(modifiedTyp)
|
||||
builder.add(" ")
|
||||
builder.add(param.loc.snippet)
|
||||
else:
|
||||
builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
|
||||
|
||||
proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add(typ)
|
||||
|
||||
proc addProcTypedParam(builder: var Builder, paramBuilder: var ProcParamBuilder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
|
||||
if paramBuilder.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
paramBuilder.needsComma = true
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
|
||||
proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
|
||||
# does not exist in NIFC, needs to be proc pragma
|
||||
if params.needsComma:
|
||||
builder.add(", ")
|
||||
else:
|
||||
params.needsComma = true
|
||||
builder.add("...")
|
||||
|
||||
template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
|
||||
params = initProcParamBuilder(builder)
|
||||
body
|
||||
finishProcParamBuilder(builder, params)
|
||||
|
||||
type SimpleProcParam = tuple
|
||||
name, typ: string
|
||||
|
||||
proc cProcParams(params: varargs[SimpleProcParam]): Snippet =
|
||||
if params.len == 0: return "(void)"
|
||||
result = "("
|
||||
for i in 0 ..< params.len:
|
||||
if i != 0: result.add(", ")
|
||||
result.add(params[i].typ)
|
||||
if params[i].name.len != 0:
|
||||
result.add(" ")
|
||||
result.add(params[i].name)
|
||||
result.add(")")
|
||||
|
||||
template addProcHeaderWithParams(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, rettype: Snippet, paramBuilder: typed) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
paramBuilder
|
||||
|
||||
proc addProcHeader(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, rettype, params: Snippet) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
addProcHeaderWithParams(builder, callConv, name, rettype):
|
||||
builder.add(params)
|
||||
|
||||
proc addProcHeader(builder: var Builder, name: string, rettype, params: Snippet, isConstructor = false) =
|
||||
# no callconv
|
||||
builder.add(rettype)
|
||||
builder.add(" ")
|
||||
if isConstructor:
|
||||
builder.add("__attribute__((constructor)) ")
|
||||
builder.add(name)
|
||||
builder.add(params)
|
||||
|
||||
proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
|
||||
# on nifc should build something like (proc name params type pragmas
|
||||
# with no body given
|
||||
# or enforce this with secondary builder object
|
||||
let noreturn = isNoReturn(m, prc)
|
||||
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(naked) ")
|
||||
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(noreturn) ")
|
||||
builder.add(CallingConvToStr[prc.typ.callConv])
|
||||
builder.add("(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
if addAttributes:
|
||||
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((naked))")
|
||||
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((noreturn))")
|
||||
|
||||
proc finishProcHeaderAsProto(builder: var Builder) =
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template finishProcHeaderWithBody(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent(" {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
builder.addNewline
|
||||
|
||||
proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
|
||||
isStatic = false, ignoreAttributes = false) =
|
||||
# on nifc, builds full variable
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
let noreturn = isNoReturn(m, prc)
|
||||
if not ignoreAttributes:
|
||||
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(naked) ")
|
||||
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add("__declspec(noreturn) ")
|
||||
builder.add(CallingConvToStr[prc.typ.callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
if not ignoreAttributes:
|
||||
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((naked))")
|
||||
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
|
||||
builder.add(" __attribute__((noreturn))")
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcVar(builder: var Builder, callConv: TCallingConvention,
|
||||
name: string, params, rettype: Snippet,
|
||||
isStatic = false, isVolatile = false) =
|
||||
# on nifc, builds full variable
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
builder.add(CallingConvToStr[callConv])
|
||||
builder.add("_PTR(")
|
||||
builder.add(rettype)
|
||||
builder.add(", ")
|
||||
if isVolatile:
|
||||
builder.add("volatile ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addProcVar(builder: var Builder,
|
||||
name: string, params, rettype: Snippet,
|
||||
isStatic = false, isVolatile = false) =
|
||||
# no callconv
|
||||
if isStatic:
|
||||
builder.add("static ")
|
||||
builder.add(rettype)
|
||||
builder.add(" (*")
|
||||
if isVolatile:
|
||||
builder.add("volatile ")
|
||||
builder.add(name)
|
||||
builder.add(")")
|
||||
builder.add(params)
|
||||
# ensure we are just adding a variable:
|
||||
builder.addLineEnd(";")
|
||||
|
||||
type VarInitializerKind = enum
|
||||
Assignment, CppConstructor
|
||||
|
||||
proc addVar(builder: var Builder, m: BModule, s: PSym, name: string, typ: Snippet, kind = Local, visibility: DeclVisibility = None, initializer: Snippet = "", initializerKind: VarInitializerKind = Assignment) =
|
||||
if sfCodegenDecl in s.flags:
|
||||
builder.add(runtimeFormat(s.cgDeclFrmt, [typ, name]))
|
||||
if initializer.len != 0:
|
||||
if initializerKind == Assignment:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
return
|
||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||
builder.add("NIM_ALIGN(" & $s.alignment & ") ")
|
||||
builder.addVisibilityPrefix(visibility)
|
||||
if kind == Threadvar:
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
builder.add("NIM_THREAD_LOCAL ")
|
||||
else: builder.add("NIM_THREADVAR ")
|
||||
else:
|
||||
builder.addVarHeader(kind)
|
||||
builder.add(typ)
|
||||
if sfRegister in s.flags: builder.add(" register")
|
||||
if sfVolatile in s.flags: builder.add(" volatile")
|
||||
if sfNoalias in s.flags: builder.add(" NIM_NOALIAS")
|
||||
builder.add(" ")
|
||||
builder.add(name)
|
||||
if initializer.len != 0:
|
||||
if initializerKind == Assignment:
|
||||
builder.add(" = ")
|
||||
builder.add(initializer)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addInclude(builder: var Builder, value: Snippet) =
|
||||
builder.addLineEnd("#include " & value)
|
||||
259
compiler/cbuilderexprs.nim
Normal file
259
compiler/cbuilderexprs.nim
Normal file
@@ -0,0 +1,259 @@
|
||||
proc constType(t: Snippet): Snippet =
|
||||
# needs manipulation of `t` in nifc
|
||||
"NIM_CONST " & t
|
||||
|
||||
proc constPtrType(t: Snippet): Snippet =
|
||||
t & "* NIM_CONST"
|
||||
|
||||
proc ptrConstType(t: Snippet): Snippet =
|
||||
"NIM_CONST " & t & "*"
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
proc cppRefType(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 procPtrTypeUnnamed(rettype, params: Snippet): Snippet =
|
||||
rettype & "(*)" & params
|
||||
|
||||
proc procPtrTypeUnnamedNimCall(rettype, params: Snippet): Snippet =
|
||||
rettype & "(N_RAW_NIMCALL*)" & params
|
||||
|
||||
proc procPtrTypeUnnamed(callConv: TCallingConvention, rettype, params: Snippet): Snippet =
|
||||
CallingConvToStr[callConv] & "_PTR(" & rettype & ", )" & params
|
||||
|
||||
type CppCaptureKind = enum None, ByReference, ByCopy
|
||||
|
||||
template addCppLambda(builder: var Builder, captures: CppCaptureKind, params: Snippet, body: typed) =
|
||||
builder.add("[")
|
||||
case captures
|
||||
of None: discard
|
||||
of ByReference: builder.add("&")
|
||||
of ByCopy: builder.add("=")
|
||||
builder.add("] ")
|
||||
builder.add(params)
|
||||
builder.addLineEndIndent(" {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc cCast(typ, value: Snippet): Snippet =
|
||||
"((" & typ & ") " & value & ")"
|
||||
|
||||
proc wrapPar(value: Snippet): Snippet =
|
||||
# used for expression group, no-op on sexp
|
||||
"(" & value & ")"
|
||||
|
||||
proc removeSinglePar(value: Snippet): Snippet =
|
||||
# removes a single paren layer expected to exist, to silence Wparentheses-equality
|
||||
assert value[0] == '(' and value[^1] == ')'
|
||||
value[1..^2]
|
||||
|
||||
template addCast(builder: var Builder, typ: Snippet, valueBody: typed) =
|
||||
## adds a cast to `typ` with value built by `valueBody`
|
||||
builder.add "(("
|
||||
builder.add typ
|
||||
builder.add ") "
|
||||
valueBody
|
||||
builder.add ")"
|
||||
|
||||
proc cAddr(value: Snippet): Snippet =
|
||||
"(&" & value & ")"
|
||||
|
||||
proc cLabelAddr(value: TLabel): Snippet =
|
||||
"&&" & value
|
||||
|
||||
proc cDeref(value: Snippet): Snippet =
|
||||
"(*" & value & ")"
|
||||
|
||||
proc subscript(a, b: Snippet): Snippet =
|
||||
a & "[" & b & "]"
|
||||
|
||||
proc dotField(a, b: Snippet): Snippet =
|
||||
a & "." & b
|
||||
|
||||
proc derefField(a, b: Snippet): Snippet =
|
||||
a & "->" & b
|
||||
|
||||
type CallBuilder = object
|
||||
needsComma: bool
|
||||
|
||||
proc initCallBuilder(builder: var Builder, callee: Snippet): CallBuilder =
|
||||
result = CallBuilder(needsComma: false)
|
||||
builder.add(callee)
|
||||
builder.add("(")
|
||||
|
||||
const cArgumentSeparator = ", "
|
||||
|
||||
proc addArgumentSeparator(builder: var Builder) =
|
||||
# no-op on NIFC
|
||||
# used by "single argument" builders
|
||||
builder.add(cArgumentSeparator)
|
||||
|
||||
template addArgument(builder: var Builder, call: var CallBuilder, valueBody: typed) =
|
||||
if call.needsComma:
|
||||
builder.addArgumentSeparator()
|
||||
else:
|
||||
call.needsComma = true
|
||||
valueBody
|
||||
|
||||
proc finishCallBuilder(builder: var Builder, call: CallBuilder) =
|
||||
builder.add(")")
|
||||
|
||||
template addCall(builder: var Builder, call: out CallBuilder, callee: Snippet, body: typed) =
|
||||
call = initCallBuilder(builder, callee)
|
||||
body
|
||||
finishCallBuilder(builder, call)
|
||||
|
||||
proc addCall(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
|
||||
builder.add(callee)
|
||||
builder.add("(")
|
||||
if args.len != 0:
|
||||
builder.add(args[0])
|
||||
for i in 1 ..< args.len:
|
||||
builder.add(", ")
|
||||
builder.add(args[i])
|
||||
builder.add(")")
|
||||
|
||||
proc cCall(callee: Snippet, args: varargs[Snippet]): Snippet =
|
||||
result = callee
|
||||
result.add("(")
|
||||
if args.len != 0:
|
||||
result.add(args[0])
|
||||
for i in 1 ..< args.len:
|
||||
result.add(", ")
|
||||
result.add(args[i])
|
||||
result.add(")")
|
||||
|
||||
proc addSizeof(builder: var Builder, val: Snippet) =
|
||||
builder.add("sizeof(")
|
||||
builder.add(val)
|
||||
builder.add(")")
|
||||
|
||||
proc addAlignof(builder: var Builder, val: Snippet) =
|
||||
builder.add("NIM_ALIGNOF(")
|
||||
builder.add(val)
|
||||
builder.add(")")
|
||||
|
||||
proc addOffsetof(builder: var Builder, val, member: Snippet) =
|
||||
builder.add("offsetof(")
|
||||
builder.add(val)
|
||||
builder.add(", ")
|
||||
builder.add(member)
|
||||
builder.add(")")
|
||||
|
||||
template cSizeof(val: Snippet): Snippet =
|
||||
"sizeof(" & val & ")"
|
||||
|
||||
template cAlignof(val: Snippet): Snippet =
|
||||
"NIM_ALIGNOF(" & val & ")"
|
||||
|
||||
template cOffsetof(val, member: Snippet): Snippet =
|
||||
"offsetof(" & val & ", " & member & ")"
|
||||
|
||||
type TypedBinaryOp = enum
|
||||
Add, Sub, Mul, Div, Mod
|
||||
Shr, Shl, BitAnd, BitOr, BitXor
|
||||
|
||||
const typedBinaryOperators: array[TypedBinaryOp, string] = [
|
||||
Add: "+",
|
||||
Sub: "-",
|
||||
Mul: "*",
|
||||
Div: "/",
|
||||
Mod: "%",
|
||||
Shr: ">>",
|
||||
Shl: "<<",
|
||||
BitAnd: "&",
|
||||
BitOr: "|",
|
||||
BitXor: "^"
|
||||
]
|
||||
|
||||
type TypedUnaryOp = enum
|
||||
Neg, BitNot
|
||||
|
||||
const typedUnaryOperators: array[TypedUnaryOp, string] = [
|
||||
Neg: "-",
|
||||
BitNot: "~",
|
||||
]
|
||||
|
||||
type UntypedBinaryOp = enum
|
||||
LessEqual, LessThan, GreaterEqual, GreaterThan, Equal, NotEqual
|
||||
And, Or
|
||||
|
||||
const untypedBinaryOperators: array[UntypedBinaryOp, string] = [
|
||||
LessEqual: "<=",
|
||||
LessThan: "<",
|
||||
GreaterEqual: ">=",
|
||||
GreaterThan: ">",
|
||||
Equal: "==",
|
||||
NotEqual: "!=",
|
||||
And: "&&",
|
||||
Or: "||"
|
||||
]
|
||||
|
||||
type UntypedUnaryOp = enum
|
||||
Not
|
||||
|
||||
const untypedUnaryOperators: array[UntypedUnaryOp, string] = [
|
||||
Not: "!"
|
||||
]
|
||||
|
||||
proc addOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(typedBinaryOperators[binOp])
|
||||
builder.add(' ')
|
||||
builder.add(b)
|
||||
builder.add(')')
|
||||
|
||||
proc addOp(builder: var Builder, unOp: TypedUnaryOp, t: Snippet, a: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(typedUnaryOperators[unOp])
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add("))")
|
||||
|
||||
proc addOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(untypedBinaryOperators[binOp])
|
||||
builder.add(' ')
|
||||
builder.add(b)
|
||||
builder.add(')')
|
||||
|
||||
proc addOp(builder: var Builder, unOp: UntypedUnaryOp, a: Snippet) =
|
||||
builder.add('(')
|
||||
builder.add(untypedUnaryOperators[unOp])
|
||||
builder.add('(')
|
||||
builder.add(a)
|
||||
builder.add("))")
|
||||
|
||||
template cOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
|
||||
'(' & a & ' ' & typedBinaryOperators[binOp] & ' ' & b & ')'
|
||||
|
||||
template cOp(binOp: TypedUnaryOp, t: Snippet, a: Snippet): Snippet =
|
||||
'(' & typedUnaryOperators[binOp] & '(' & a & "))"
|
||||
|
||||
template cOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
|
||||
'(' & a & ' ' & untypedBinaryOperators[binOp] & ' ' & b & ')'
|
||||
|
||||
template cOp(binOp: UntypedUnaryOp, a: Snippet): Snippet =
|
||||
'(' & untypedUnaryOperators[binOp] & '(' & a & "))"
|
||||
|
||||
template cIfExpr(cond, a, b: Snippet): Snippet =
|
||||
# XXX used for `min` and `max`, maybe add nifc primitives for these
|
||||
"(" & cond & " ? " & a & " : " & b & ")"
|
||||
|
||||
template cUnlikely(val: Snippet): Snippet =
|
||||
"NIM_UNLIKELY(" & val & ")"
|
||||
329
compiler/cbuilderstmts.nim
Normal file
329
compiler/cbuilderstmts.nim
Normal file
@@ -0,0 +1,329 @@
|
||||
template addAssignmentWithValue(builder: var Builder, lhs: Snippet, valueBody: typed) =
|
||||
builder.add(lhs)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addFieldAssignmentWithValue(builder: var Builder, lhs: Snippet, name: string, valueBody: typed) =
|
||||
builder.add(lhs)
|
||||
builder.add("." & name & " = ")
|
||||
valueBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addAssignment(builder: var Builder, lhs, rhs: Snippet) =
|
||||
builder.addAssignmentWithValue(lhs):
|
||||
builder.add(rhs)
|
||||
|
||||
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.add(rhs)
|
||||
|
||||
template addMutualFieldAssignment(builder: var Builder, lhs, rhs: Snippet, name: string) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.add(rhs)
|
||||
builder.add("." & name)
|
||||
|
||||
template addAssignment(builder: var Builder, lhs: Snippet, rhs: int | int64 | uint64 | Int128) =
|
||||
builder.addAssignmentWithValue(lhs):
|
||||
builder.addIntValue(rhs)
|
||||
|
||||
template addFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: int | int64 | uint64 | Int128) =
|
||||
builder.addFieldAssignmentWithValue(lhs, name):
|
||||
builder.addIntValue(rhs)
|
||||
|
||||
template addDerefFieldAssignment(builder: var Builder, lhs: Snippet, name: string, rhs: Snippet) =
|
||||
builder.add(lhs)
|
||||
builder.add("->" & name & " = ")
|
||||
builder.add(rhs)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addSubscriptAssignment(builder: var Builder, lhs: Snippet, index: Snippet, rhs: Snippet) =
|
||||
builder.add(lhs)
|
||||
builder.add("[" & index & "] = ")
|
||||
builder.add(rhs)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
template addStmt(builder: var Builder, stmtBody: typed) =
|
||||
## makes an expression built by `stmtBody` into a statement
|
||||
stmtBody
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addCallStmt(builder: var Builder, callee: Snippet, args: varargs[Snippet]) =
|
||||
builder.addStmt():
|
||||
builder.addCall(callee, args)
|
||||
|
||||
# XXX blocks need indent tracker in `Builder` object
|
||||
|
||||
template addSingleIfStmt(builder: var Builder, cond: Snippet, body: typed) =
|
||||
builder.add("if (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
template addSingleIfStmtWithCond(builder: var Builder, condBody: typed, body: typed) =
|
||||
builder.add("if (")
|
||||
condBody
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc initIfStmt(builder: var Builder): IfBuilder =
|
||||
IfBuilder(state: WaitingIf)
|
||||
|
||||
proc finishIfStmt(builder: var Builder, stmt: IfBuilder) =
|
||||
assert stmt.state != InBlock
|
||||
builder.addNewline()
|
||||
|
||||
template addIfStmt(builder: var Builder, stmt: out IfBuilder, body: typed) =
|
||||
stmt = initIfStmt(builder)
|
||||
body
|
||||
finishIfStmt(builder, stmt)
|
||||
|
||||
proc initElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet) =
|
||||
case stmt.state
|
||||
of WaitingIf:
|
||||
builder.add("if (")
|
||||
of WaitingElseIf:
|
||||
builder.add(" else if (")
|
||||
else: assert false, $stmt.state
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
stmt.state = InBlock
|
||||
|
||||
proc initElseBranch(builder: var Builder, stmt: var IfBuilder) =
|
||||
assert stmt.state == WaitingElseIf, $stmt.state
|
||||
builder.addLineEndIndent(" else {")
|
||||
stmt.state = InBlock
|
||||
|
||||
proc finishBranch(builder: var Builder, stmt: var IfBuilder) =
|
||||
builder.addDedent("}")
|
||||
stmt.state = WaitingElseIf
|
||||
|
||||
template addElifBranch(builder: var Builder, stmt: var IfBuilder, cond: Snippet, body: typed) =
|
||||
initElifBranch(builder, stmt, cond)
|
||||
body
|
||||
finishBranch(builder, stmt)
|
||||
|
||||
template addElseBranch(builder: var Builder, stmt: var IfBuilder, body: typed) =
|
||||
initElseBranch(builder, stmt)
|
||||
body
|
||||
finishBranch(builder, stmt)
|
||||
|
||||
proc initForRange(builder: var Builder, i, start, bound: Snippet, inclusive: bool = false) =
|
||||
builder.add("for (")
|
||||
builder.add(i)
|
||||
builder.add(" = ")
|
||||
builder.add(start)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
if inclusive:
|
||||
builder.add(" <= ")
|
||||
else:
|
||||
builder.add(" < ")
|
||||
builder.add(bound)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
builder.addLineEndIndent("++) {")
|
||||
|
||||
proc initForStep(builder: var Builder, i, start, bound, step: Snippet, inclusive: bool = false) =
|
||||
builder.add("for (")
|
||||
builder.add(i)
|
||||
builder.add(" = ")
|
||||
builder.add(start)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
if inclusive:
|
||||
builder.add(" <= ")
|
||||
else:
|
||||
builder.add(" < ")
|
||||
builder.add(bound)
|
||||
builder.add("; ")
|
||||
builder.add(i)
|
||||
builder.add(" += ")
|
||||
builder.add(step)
|
||||
builder.addLineEndIndent(") {")
|
||||
|
||||
proc finishFor(builder: var Builder) {.inline.} =
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
template addForRangeExclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
|
||||
initForRange(builder, i, start, bound, false)
|
||||
body
|
||||
finishFor(builder)
|
||||
|
||||
template addForRangeInclusive(builder: var Builder, i, start, bound: Snippet, body: typed) =
|
||||
initForRange(builder, i, start, bound, true)
|
||||
body
|
||||
finishFor(builder)
|
||||
|
||||
template addSwitchStmt(builder: var Builder, val: Snippet, body: typed) =
|
||||
builder.add("switch (")
|
||||
builder.add(val)
|
||||
builder.addLineEnd(") {") # no indent
|
||||
body
|
||||
builder.addLineEnd("}")
|
||||
|
||||
template addSingleSwitchCase(builder: var Builder, val: Snippet, body: typed) =
|
||||
builder.add("case ")
|
||||
builder.add(val)
|
||||
builder.addLineEndIndent(":")
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
type
|
||||
SwitchCaseState = enum
|
||||
None, Of, Else, Finished
|
||||
SwitchCaseBuilder = object
|
||||
state: SwitchCaseState
|
||||
|
||||
proc addCase(builder: var Builder, info: var SwitchCaseBuilder, val: Snippet) =
|
||||
if info.state != Of:
|
||||
assert info.state == None
|
||||
info.state = Of
|
||||
builder.add("case ")
|
||||
builder.add(val)
|
||||
builder.addLineEndIndent(":")
|
||||
|
||||
proc addCaseRange(builder: var Builder, info: var SwitchCaseBuilder, first, last: Snippet) =
|
||||
if info.state != Of:
|
||||
assert info.state == None
|
||||
info.state = Of
|
||||
builder.add("case ")
|
||||
builder.add(first)
|
||||
builder.add(" ... ")
|
||||
builder.add(last)
|
||||
builder.addLineEndIndent(":")
|
||||
|
||||
proc addCaseElse(builder: var Builder, info: var SwitchCaseBuilder) =
|
||||
assert info.state == None
|
||||
info.state = Else
|
||||
builder.addLineEndIndent("default:")
|
||||
|
||||
template addSwitchCase(builder: var Builder, info: out SwitchCaseBuilder, caseBody, body: typed) =
|
||||
info = SwitchCaseBuilder(state: None)
|
||||
caseBody
|
||||
info.state = Finished
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
template addSwitchElse(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent("default:")
|
||||
body
|
||||
builder.addLineEndDedent("")
|
||||
|
||||
proc addBreak(builder: var Builder) =
|
||||
builder.addLineEnd("break;")
|
||||
|
||||
type ScopeBuilder = object
|
||||
inside: bool
|
||||
|
||||
proc initScope(builder: var Builder): ScopeBuilder =
|
||||
builder.addLineEndIndent("{")
|
||||
result = ScopeBuilder(inside: true)
|
||||
|
||||
proc finishScope(builder: var Builder, scope: var ScopeBuilder) =
|
||||
assert scope.inside, "scope not inited"
|
||||
builder.addLineEndDedent("}")
|
||||
scope.inside = false
|
||||
|
||||
template addScope(builder: var Builder, body: typed) =
|
||||
builder.addLineEndIndent("{")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
type WhileBuilder = object
|
||||
inside: bool
|
||||
|
||||
proc initWhileStmt(builder: var Builder, cond: Snippet): WhileBuilder =
|
||||
builder.add("while (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
result = WhileBuilder(inside: true)
|
||||
|
||||
proc finishWhileStmt(builder: var Builder, stmt: var WhileBuilder) =
|
||||
assert stmt.inside, "while stmt not inited"
|
||||
builder.addLineEndDedent("}")
|
||||
stmt.inside = false
|
||||
|
||||
template addWhileStmt(builder: var Builder, cond: Snippet, body: typed) =
|
||||
builder.add("while (")
|
||||
builder.add(cond)
|
||||
builder.addLineEndIndent(") {")
|
||||
body
|
||||
builder.addLineEndDedent("}")
|
||||
|
||||
proc addLabel(builder: var Builder, name: TLabel) =
|
||||
builder.add(name)
|
||||
builder.addLineEnd(": ;")
|
||||
|
||||
proc addReturn(builder: var Builder) =
|
||||
builder.addLineEnd("return;")
|
||||
|
||||
proc addReturn(builder: var Builder, value: Snippet) =
|
||||
builder.add("return ")
|
||||
builder.add(value)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addGoto(builder: var Builder, label: TLabel) =
|
||||
builder.add("goto ")
|
||||
builder.add(label)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addComputedGoto(builder: var Builder, value: Snippet) =
|
||||
builder.add("goto *")
|
||||
builder.add(value)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addIncr(builder: var Builder, val: Snippet) =
|
||||
builder.add(val)
|
||||
builder.addLineEnd("++;")
|
||||
|
||||
proc addDecr(builder: var Builder, val: Snippet) =
|
||||
builder.add(val)
|
||||
builder.addLineEnd("--;")
|
||||
|
||||
proc addInPlaceOp(builder: var Builder, binOp: TypedBinaryOp, t: Snippet, a, b: Snippet) =
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(typedBinaryOperators[binOp])
|
||||
builder.add("= ")
|
||||
builder.add(b)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc addInPlaceOp(builder: var Builder, binOp: UntypedBinaryOp, a, b: Snippet) =
|
||||
builder.add(a)
|
||||
builder.add(' ')
|
||||
builder.add(untypedBinaryOperators[binOp])
|
||||
builder.add("= ")
|
||||
builder.add(b)
|
||||
builder.addLineEnd(";")
|
||||
|
||||
proc cInPlaceOp(binOp: TypedBinaryOp, t: Snippet, a, b: Snippet): Snippet =
|
||||
result = ""
|
||||
result.add(a)
|
||||
result.add(' ')
|
||||
result.add(typedBinaryOperators[binOp])
|
||||
result.add("= ")
|
||||
result.add(b)
|
||||
result.add(";\n")
|
||||
|
||||
proc cInPlaceOp(binOp: UntypedBinaryOp, a, b: Snippet): Snippet =
|
||||
result = ""
|
||||
result.add(a)
|
||||
result.add(' ')
|
||||
result.add(untypedBinaryOperators[binOp])
|
||||
result.add("= ")
|
||||
result.add(b)
|
||||
result.add(";\n")
|
||||
|
||||
template addCPragma(builder: var Builder, val: Snippet) =
|
||||
builder.addNewline()
|
||||
builder.add("#pragma ")
|
||||
builder.add(val)
|
||||
builder.addNewline()
|
||||
|
||||
proc addDiscard(builder: var Builder, val: Snippet) =
|
||||
builder.add("(void)")
|
||||
builder.add(val)
|
||||
builder.addLineEnd(";")
|
||||
@@ -84,20 +84,21 @@ proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
|
||||
let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
|
||||
var op = initLocExpr(p, newSymNode(dtor))
|
||||
var callee = rdLoc(op)
|
||||
let destroy = if dtor.typ.firstParamType.kind == tyVar:
|
||||
callee & "(&" & rdLoc(tmp) & ")"
|
||||
let destroyArg =
|
||||
if dtor.typ.firstParamType.kind == tyVar:
|
||||
cAddr(rdLoc(tmp))
|
||||
else:
|
||||
callee & "(" & rdLoc(tmp) & ")"
|
||||
rdLoc(tmp)
|
||||
let destroy = cCall(callee, destroyArg)
|
||||
raiseExitCleanup(p, destroy)
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
callee, params: Rope) =
|
||||
result: var Builder, call: var CallBuilder) =
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
genLineDir(p, ri)
|
||||
var pl = callee & "(" & params
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ.returnType != nil:
|
||||
@@ -106,7 +107,6 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
# perhaps generate no temp if the call doesn't have side effects
|
||||
flags.incl needTempForOpenArray
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if params.len != 0: pl.add(", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
||||
# Great, we can use 'd':
|
||||
@@ -114,33 +114,39 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
let rad = addrLoc(p.config, d)
|
||||
result.addArgument(call):
|
||||
result.add(rad)
|
||||
result.finishCallBuilder(call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
let ratmp = addrLoc(p.config, tmp)
|
||||
result.addArgument(call):
|
||||
result.add(ratmp)
|
||||
result.finishCallBuilder(call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
pl.add(")")
|
||||
result.finishCallBuilder(call)
|
||||
if p.module.compileToCpp:
|
||||
if lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.snippet = pl
|
||||
d.snippet = extract(result)
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone and p.splitDecls == 0:
|
||||
d = getTempCpp(p, typ.returnType, pl)
|
||||
d = getTempCpp(p, typ.returnType, extract(result))
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = pl
|
||||
list.snippet = extract(result)
|
||||
genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
@@ -151,7 +157,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = pl
|
||||
list.snippet = extract(result)
|
||||
genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
|
||||
if canRaise:
|
||||
if not (useTemp and cleanupTemp(p, typ.returnType, d)):
|
||||
@@ -159,15 +165,16 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
var list = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = pl
|
||||
list.snippet = extract(result)
|
||||
genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
|
||||
if canRaise:
|
||||
if not cleanupTemp(p, typ.returnType, tmp):
|
||||
raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
else:
|
||||
pl.add(");\n")
|
||||
line(p, cpsStmts, pl)
|
||||
finishCallBuilder(result, call)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(result))
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
|
||||
@@ -199,49 +206,49 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c, ty)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
let ra = rdLoc(a)
|
||||
let rb = rdLoc(b)
|
||||
let rc = rdLoc(c)
|
||||
let lengthExpr = cOp(Add, NimInt, cOp(Sub, NimInt, rc, rb), cIntValue(1))
|
||||
case ty.kind
|
||||
of tyArray:
|
||||
let first = toInt64(firstOrd(p.config, ty))
|
||||
if first == 0:
|
||||
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
|
||||
else:
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(first, lit)
|
||||
result = ("($4*)($1)+(($2)-($3))" %
|
||||
[rdLoc(a), rdLoc(b), lit, dest],
|
||||
lengthExpr)
|
||||
let lit = cIntLiteral(first)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, cOp(Sub, NimInt, rb, lit))), lengthExpr)
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(q[1]):
|
||||
result = ("($3*)($1.Field0)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
let data = if reifiedOpenArray(q[1]): dotField(ra, "Field0") else: ra
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, data, rb)), lengthExpr)
|
||||
of tyUncheckedArray, tyCstring:
|
||||
result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
result = (cCast(ptrType(dest), cOp(Add, NimInt, ra, rb)), lengthExpr)
|
||||
of tyString, tySequence:
|
||||
let atyp = skipTypes(a.t, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
var val: Snippet
|
||||
if atyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
|
||||
lengthExpr)
|
||||
val = cDeref(ra)
|
||||
else:
|
||||
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
|
||||
lengthExpr)
|
||||
val = ra
|
||||
result = (
|
||||
cIfExpr(dataFieldAccessor(p, val),
|
||||
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
|
||||
NimNil),
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("", "")
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
|
||||
var q = skipConv(n)
|
||||
var skipped = false
|
||||
while q.kind == nkStmtListExpr and q.len > 0:
|
||||
@@ -256,42 +263,66 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
genStmts(p, q[i])
|
||||
q = q.lastSon
|
||||
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
|
||||
result.add x & ", " & y
|
||||
result.add(x)
|
||||
result.addArgumentSeparator()
|
||||
result.add(y)
|
||||
else:
|
||||
var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
let ra = rdLoc(a)
|
||||
if reifiedOpenArray(n):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
result.add(derefField(ra, "Field0"))
|
||||
result.addArgumentSeparator()
|
||||
result.add(derefField(ra, "Field1"))
|
||||
else:
|
||||
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
result.add(dotField(ra, "Field0"))
|
||||
result.addArgumentSeparator()
|
||||
result.add(dotField(ra, "Field1"))
|
||||
else:
|
||||
result.add "$1, $1Len_0" % [rdLoc(a)]
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.add(ra & "Len_0")
|
||||
of tyString, tySequence:
|
||||
let ntyp = skipTypes(n.typ, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let bra = byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||
bra)
|
||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
|
||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
let ra = a.rdLoc
|
||||
var t = TLoc(snippet: cDeref(ra))
|
||||
let lt = lenExpr(p, t)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(lt)
|
||||
else:
|
||||
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
|
||||
let ra = a.rdLoc
|
||||
let la = lenExpr(p, a)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(la)
|
||||
of tyArray:
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||
let ra = rdLoc(a)
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.addIntValue(lengthOrd(p.config, a.t))
|
||||
of tyPtr, tyRef:
|
||||
case elementType(a.t).kind
|
||||
of tyString, tySequence:
|
||||
var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
|
||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
let ra = a.rdLoc
|
||||
var t = TLoc(snippet: cDeref(ra))
|
||||
let lt = lenExpr(p, t)
|
||||
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
|
||||
result.addArgumentSeparator()
|
||||
result.add(lt)
|
||||
of tyArray:
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))]
|
||||
let ra = rdLoc(a)
|
||||
result.add(ra)
|
||||
result.addArgumentSeparator()
|
||||
result.addIntValue(lengthOrd(p.config, elementType(a.t)))
|
||||
else:
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
@@ -311,11 +342,12 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
result = getTemp(p, a.lode.typ, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
|
||||
var a = initLocExpr(p, n[0])
|
||||
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||
let ra = withTmpIfNeeded(p, a, needsTmp).rdLoc
|
||||
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
@@ -336,7 +368,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
# variable. Thus, we create a temporary pointer variable instead.
|
||||
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
|
||||
if needsIndirect:
|
||||
n.typ = n.typ.exactReplica
|
||||
n.typ() = n.typ.exactReplica
|
||||
n.typ.flags.incl tfVarIsPtr
|
||||
a = initLocExprSingleUse(p, n)
|
||||
a = withTmpIfNeeded(p, a, needsTmp)
|
||||
@@ -353,15 +385,14 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
addRdLoc(a, result)
|
||||
else:
|
||||
a = initLocExprSingleUse(p, n)
|
||||
if param.typ.kind in abstractPtrs:
|
||||
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
a.snippet = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
|
||||
a.snippet = cCast(getTypeDesc(p.module, param.typ), rdCharLoc(a))
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
#assert result != nil
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Builder; needsTmp = false) =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
@@ -424,7 +455,7 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
||||
for s in n:
|
||||
getPotentialReads(s, result)
|
||||
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Builder, argBuilder: var CallBuilder) =
|
||||
# We must generate temporaries in cases like #14396
|
||||
# to keep the strict Left-To-Right evaluation
|
||||
var needTmp = newSeq[bool](ri.len - 1)
|
||||
@@ -446,21 +477,22 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
|
||||
var oldLen = result.len
|
||||
for i in 1..<ri.len:
|
||||
if i < typ.n.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
let paramType = typ.n[i]
|
||||
if not paramType.typ.isCompileTimeOnly:
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
|
||||
var arg = newBuilder("")
|
||||
genArg(p, ri[i], paramType.sym, ri, arg, needTmp[i-1])
|
||||
if arg.buf.len != 0:
|
||||
result.addArgument(argBuilder):
|
||||
result.add(extract(arg))
|
||||
else:
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArgNoParam(p, ri[i], result, needTmp[i-1])
|
||||
var arg = newBuilder("")
|
||||
genArgNoParam(p, ri[i], arg, needTmp[i-1])
|
||||
if arg.buf.len != 0:
|
||||
result.addArgument(argBuilder):
|
||||
result.add(extract(arg))
|
||||
|
||||
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
||||
@@ -474,21 +506,39 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
var params = newRopeAppender()
|
||||
genParams(p, ri, typ, params)
|
||||
|
||||
var callee = rdLoc(op)
|
||||
if p.hcrOn and ri[0].kind == nkSym:
|
||||
callee.addActualSuffixForHCR(p.module.module, ri[0].sym)
|
||||
fixupCall(p, le, ri, d, callee, params)
|
||||
|
||||
var res = newBuilder("")
|
||||
var call = initCallBuilder(res, callee)
|
||||
genParams(p, ri, typ, res, call)
|
||||
fixupCall(p, le, ri, d, res, call)
|
||||
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
proc addComma(r: Rope): Rope =
|
||||
if r.len == 0: r else: r & ", "
|
||||
template callProc(rp, params, pTyp: Snippet): Snippet =
|
||||
let e = dotField(rp, "ClE_0")
|
||||
let p = dotField(rp, "ClP_0")
|
||||
let eCall =
|
||||
# note `params` here is actually multiple params
|
||||
if params.len == 0:
|
||||
cCall(p, e)
|
||||
else:
|
||||
cCall(p, params, e)
|
||||
cIfExpr(e,
|
||||
eCall,
|
||||
cCall(cCast(pTyp, p), params))
|
||||
|
||||
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
|
||||
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
|
||||
template callIter(rp, params: Snippet): Snippet =
|
||||
# we know the env exists
|
||||
let e = dotField(rp, "ClE_0")
|
||||
let p = dotField(rp, "ClP_0")
|
||||
# note `params` here is actually multiple params
|
||||
if params.len == 0:
|
||||
cCall(p, e)
|
||||
else:
|
||||
cCall(p, params, e)
|
||||
|
||||
var op = initLocExpr(p, ri[0])
|
||||
|
||||
@@ -496,20 +546,23 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var typ = skipTypes(ri[0].typ, abstractInstOwned)
|
||||
assert(typ.kind == tyProc)
|
||||
|
||||
var pl = newRopeAppender()
|
||||
genParams(p, ri, typ, pl)
|
||||
var params = newBuilder("")
|
||||
var argBuilder = default(CallBuilder) # not initCallBuilder, we just want the params
|
||||
genParams(p, ri, typ, params, argBuilder)
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
if tfIterator in typ.flags:
|
||||
lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||
else:
|
||||
lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
p.s(cpsStmts).addStmt():
|
||||
if tfIterator in typ.flags:
|
||||
p.s(cpsStmts).add(callIter(rp, pars))
|
||||
else:
|
||||
p.s(cpsStmts).add(callProc(rp, pars, rawProc))
|
||||
|
||||
let rawProc = getClosureType(p.module, typ, clHalf)
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
if typ.returnType != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if ri.len > 1: pl.add(", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
||||
# Great, we can use 'd':
|
||||
@@ -518,12 +571,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
params.addArgument(argBuilder):
|
||||
params.add(addrLoc(p.config, d))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
params.addArgument(argBuilder):
|
||||
params.add(addrLoc(p.config, tmp))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
@@ -531,20 +586,24 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
if tfIterator in typ.flags:
|
||||
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callIter(rp, pars)
|
||||
else:
|
||||
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callProc(rp, pars, rawProc)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
let rp = rdLoc(op)
|
||||
let pars = extract(params)
|
||||
if tfIterator in typ.flags:
|
||||
list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callIter(rp, pars)
|
||||
else:
|
||||
list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
|
||||
list.snippet = callProc(rp, pars, rawProc)
|
||||
genAssignment(p, tmp, list, {})
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
@@ -552,8 +611,8 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
||||
argsCounter: var int) =
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder;
|
||||
argBuilder: var CallBuilder) =
|
||||
if i < typ.n.len:
|
||||
# 'var T' is 'T&' in C++. This means we ignore the request of
|
||||
# any nkHiddenAddr when it's a 'var T'.
|
||||
@@ -562,20 +621,17 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
||||
if paramType.typ.isCompileTimeOnly:
|
||||
discard
|
||||
elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
inc argsCounter
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
else:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
inc argsCounter
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
else:
|
||||
if tfVarargs notin typ.flags:
|
||||
localError(p.config, ri.info, "wrong argument count")
|
||||
else:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result)
|
||||
inc argsCounter
|
||||
result.addArgument(argBuilder):
|
||||
genArgNoParam(p, ri[i], result)
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
@@ -632,7 +688,7 @@ proc skipAddrDeref(node: PNode): PNode =
|
||||
else:
|
||||
result = node
|
||||
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) =
|
||||
# for better or worse c2nim translates the 'this' argument to a 'var T'.
|
||||
# However manual wrappers may also use 'ptr T'. In any case we support both
|
||||
# for convenience.
|
||||
@@ -667,15 +723,15 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||
genArgNoParam(p, ri, result) #, typ.n[i].sym)
|
||||
result.add(".")
|
||||
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) =
|
||||
var i = 0
|
||||
var j = 1
|
||||
while i < pat.len:
|
||||
case pat[i]
|
||||
of '@':
|
||||
var argsCounter = 0
|
||||
var callBuilder = default(CallBuilder) # not init call builder
|
||||
for k in j..<ri.len:
|
||||
genOtherArg(p, ri, k, typ, result, argsCounter)
|
||||
genOtherArg(p, ri, k, typ, result, callBuilder)
|
||||
inc i
|
||||
of '#':
|
||||
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
||||
@@ -685,11 +741,11 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
|
||||
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
|
||||
result.add("(")
|
||||
if 1 < ri.len:
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, 1, typ, result, argsCounterB)
|
||||
var callBuilder: CallBuilder = default(CallBuilder)
|
||||
genOtherArg(p, ri, 1, typ, result, callBuilder)
|
||||
for k in j+1..<ri.len:
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, k, typ, result, argsCounterB)
|
||||
var callBuilder: CallBuilder = default(CallBuilder)
|
||||
genOtherArg(p, ri, k, typ, result, callBuilder)
|
||||
result.add(")")
|
||||
else:
|
||||
localError(p.config, ri.info, "call expression expected for C++ pattern")
|
||||
@@ -703,15 +759,15 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
|
||||
genArgNoParam(p, arg, result)
|
||||
#result.add debugTree(arg, 0, 10)
|
||||
else:
|
||||
var argsCounter = 0
|
||||
genOtherArg(p, ri, j, typ, result, argsCounter)
|
||||
var callBuilder = default(CallBuilder) # not init call builder
|
||||
genOtherArg(p, ri, j, typ, result, callBuilder)
|
||||
inc j
|
||||
inc i
|
||||
of '\'':
|
||||
var idx, stars: int = 0
|
||||
if scanCppGenericSlot(pat, i, idx, stars):
|
||||
var t = resolveStarsInCppType(typ, idx, stars)
|
||||
if t == nil: result.add("void")
|
||||
if t == nil: result.add(CVoid)
|
||||
else: result.add(getTypeDesc(p.module, t))
|
||||
else:
|
||||
let start = i
|
||||
@@ -730,7 +786,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
let pat = $ri[0].sym.loc.snippet
|
||||
internalAssert p.config, pat.len > 0
|
||||
if pat.contains({'#', '(', '@', '\''}):
|
||||
var pl = newRopeAppender()
|
||||
var pl = newBuilder("")
|
||||
genPatternCall(p, ri, pat, typ, pl)
|
||||
# simpler version of 'fixupCall' that works with the pl+params combination:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
@@ -740,32 +796,32 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
# with them to prevent undefined behaviour and because the codegen
|
||||
# is free to emit expressions multiple times!
|
||||
d.k = locCall
|
||||
d.snippet = pl
|
||||
d.snippet = extract(pl)
|
||||
excl d.flags, lfSingleUse
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
|
||||
list.snippet = pl
|
||||
list.snippet = extract(pl)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add(";\n")
|
||||
line(p, cpsStmts, pl)
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
else:
|
||||
var pl = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
var pl = newBuilder("")
|
||||
if 1 < ri.len:
|
||||
genThisArg(p, ri, 1, typ, pl)
|
||||
pl.add(op.snippet)
|
||||
var params = newRopeAppender()
|
||||
var res = newBuilder("")
|
||||
var call = initCallBuilder(res, extract(pl))
|
||||
for i in 2..<ri.len:
|
||||
genOtherArg(p, ri, i, typ, params, argsCounter)
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
genOtherArg(p, ri, i, typ, res, call)
|
||||
fixupCall(p, le, ri, d, res, call)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
var op = initLocExpr(p, ri[0])
|
||||
var pl = "["
|
||||
var pl = newBuilder("[")
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
@@ -807,24 +863,27 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add("Result: ")
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
else:
|
||||
var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("]")
|
||||
if d.k == locNone: d = getTemp(p, typ.returnType)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc = initLoc(locCall, ri, OnUnknown)
|
||||
list.snippet = pl
|
||||
list.snippet = extract(pl)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("];\n")
|
||||
line(p, cpsStmts, pl)
|
||||
pl.add("]")
|
||||
p.s(cpsStmts).addStmt():
|
||||
p.s(cpsStmts).add(extract(pl))
|
||||
|
||||
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||
let r = getRoot(n)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,52 +36,84 @@ proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
cgsym(m, "TGenericSeq")
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
|
||||
[tmp, makeCString(s), rope(s.len)])
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(AlwaysConst, name = tmp):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "Sup", typ = "TGenericSeq")
|
||||
res.addArrayField(name = "data", elementType = NimChar, 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.addIntValue(s.len)
|
||||
res.addField(seqInit, name = "reserved"):
|
||||
res.add(cCast(NimInt, cOp(BitOr, NimUint, cCast(NimUint, cIntValue(s.len)), NimStrlitFlag)))
|
||||
res.addField(strInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Builder) =
|
||||
if s.isNil:
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
else:
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var name: string = ""
|
||||
if id == m.labels:
|
||||
# string literal not found in the cache:
|
||||
appcg(m, result, "((#NimStringDesc*) &", [])
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, result)
|
||||
result.add ")"
|
||||
genStringLiteralDataOnlyV1(m, n.strVal, name)
|
||||
else:
|
||||
appcg(m, result, "((#NimStringDesc*) &$1$2)",
|
||||
[m.tmpBase, id])
|
||||
name = m.tmpBase & $id
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), cAddr(name)))
|
||||
|
||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
||||
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: "")])
|
||||
var res = newBuilder("")
|
||||
res.addVarWithTypeAndInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = result):
|
||||
res.addSimpleStruct(m, name = "", baseType = ""):
|
||||
res.addField(name = "cap", typ = NimInt)
|
||||
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
|
||||
do:
|
||||
var structInit: StructInitializer
|
||||
res.addStructInitializer(structInit, kind = siOrderedStruct):
|
||||
res.addField(structInit, name = "cap"):
|
||||
res.add(cOp(BitOr, NimInt, cIntValue(s.len), NimStrlitFlag))
|
||||
res.addField(structInit, name = "data"):
|
||||
res.add(makeCString(s))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
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:
|
||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
||||
litName = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, litName, isConst)
|
||||
else:
|
||||
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: "")])
|
||||
litName = m.tmpBase & $id
|
||||
let tmp = getTempName(m)
|
||||
result.add tmp
|
||||
var res = newBuilder("")
|
||||
res.addVarWithInitializer(
|
||||
if isConst: AlwaysConst else: Global,
|
||||
name = tmp,
|
||||
typ = "NimStringV2"):
|
||||
var strInit: StructInitializer
|
||||
res.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
res.addField(strInit, name = "len"):
|
||||
res.addIntValue(n.strVal.len)
|
||||
res.addField(strInit, name = "p"):
|
||||
res.add(cCast(ptrType("NimStrPayload"), cAddr(litName)))
|
||||
m.s[cfsStrData].add(extract(res))
|
||||
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
|
||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
||||
var pureLit: Rope
|
||||
if id == m.labels:
|
||||
@@ -92,7 +124,12 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Ro
|
||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
||||
else:
|
||||
pureLit = m.tmpBase & rope(id)
|
||||
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
|
||||
var strInit: StructInitializer
|
||||
result.addStructInitializer(strInit, kind = siOrderedStruct):
|
||||
result.addField(strInit, name = "len"):
|
||||
result.addIntValue(n.strVal.len)
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
@@ -107,10 +144,10 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Builder) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralV1(m, n, result)
|
||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
||||
|
||||
@@ -27,25 +27,28 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||
if disc.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
|
||||
let discField = dotField(accessor, disc.loc.snippet)
|
||||
p.s(cpsStmts).addSwitchStmt(discField):
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
var caseBuilder: SwitchCaseBuilder
|
||||
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(p, branch, caseBuilder)
|
||||
else:
|
||||
p.s(cpsStmts).addCaseElse(caseBuilder)
|
||||
do:
|
||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
||||
p.s(cpsStmts).addBreak()
|
||||
specializeResetT(p, discField, disc.loc.t)
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.snippet == "": fillObjectFields(p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(p.config, n.info, "specializeResetN()")
|
||||
specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
|
||||
specializeResetT(p, dotField(accessor, field.loc.snippet), field.loc.t)
|
||||
else: internalError(p.config, n.info, "specializeResetN()")
|
||||
|
||||
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
@@ -58,10 +61,8 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
of tyArray:
|
||||
let arraySize = lengthOrd(p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, arraySize])
|
||||
specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
||||
specializeResetT(p, subscript(accessor, i.snippet), typ.elementType)
|
||||
of tyObject:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
@@ -70,33 +71,39 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
|
||||
specializeResetT(p, dotField(accessor, "Field" & $i), a)
|
||||
|
||||
of tyString, tyRef, tySequence:
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
||||
cCast(ptrType(CPointer), cAddr(accessor)),
|
||||
NimNil)
|
||||
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "unsureAsgnRef"),
|
||||
cCast(ptrType(CPointer), cAddr(dotField(accessor, "ClE_0"))),
|
||||
NimNil)
|
||||
p.s(cpsStmts).addFieldAssignment(accessor, "ClP_0", NimNil)
|
||||
else:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
||||
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, NimNil)
|
||||
of tySet:
|
||||
case mapSetType(p.config, typ)
|
||||
of ctArray:
|
||||
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
||||
[accessor, getTypeDesc(p.module, typ)])
|
||||
let t = getTypeDesc(p.module, typ)
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
||||
accessor,
|
||||
cSizeof(t))
|
||||
of ctInt8, ctInt16, ctInt32, ctInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
p.s(cpsStmts).addAssignment(accessor, cIntValue(0))
|
||||
else:
|
||||
raiseAssert "unexpected set type kind"
|
||||
of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
||||
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
|
||||
discard
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,12 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
|
||||
p.flags.incl threadVarAccessed
|
||||
incl p.module.flags, usesThreadVars
|
||||
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
||||
p.procSec(cpsInit).add(
|
||||
ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
|
||||
p.procSec(cpsLocals).addVar(kind = Local,
|
||||
name = "NimTV_",
|
||||
typ = ptrType("NimThreadVars"))
|
||||
p.procSec(cpsInit).addAssignment("NimTV_",
|
||||
cCast(ptrType("NimThreadVars"),
|
||||
cCall(cgsymValue(p.module, "GetThreadLocalVars"))))
|
||||
|
||||
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
if emulatedThreadVars(m.config):
|
||||
@@ -30,30 +33,32 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
# allocator for it :-(
|
||||
if not containsOrIncl(m.g.nimtvDeclared, s.id):
|
||||
m.g.nimtvDeps.add(s.loc.t)
|
||||
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet])
|
||||
m.g.nimtv.addField(name = s.loc.snippet, typ = getTypeDesc(m, s.loc.t))
|
||||
else:
|
||||
if isExtern: m.s[cfsVars].add("extern ")
|
||||
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
|
||||
else: m.s[cfsVars].add("N_LIB_PRIVATE ")
|
||||
if optThreads in m.config.globalOptions:
|
||||
let sym = s.typ.sym
|
||||
if sym != nil and sfCppNonPod in sym.flags:
|
||||
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
|
||||
else: m.s[cfsVars].add("NIM_THREADVAR ")
|
||||
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
|
||||
m.s[cfsVars].addf(" $1;$n", [s.loc.snippet])
|
||||
let vis =
|
||||
if isExtern: Extern
|
||||
elif lfExportLib in s.loc.flags: ExportLibVar
|
||||
else: Private
|
||||
m.s[cfsVars].addVar(m, s,
|
||||
name = s.loc.snippet,
|
||||
typ = getTypeDesc(m, s.loc.t),
|
||||
kind = Threadvar,
|
||||
visibility = vis)
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
if m.g.nimtv.buf.len != 0 and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
||||
finishTypeDescriptions(m)
|
||||
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
m.s[cfsSeqTypes].addTypedef(name = "NimThreadVars"):
|
||||
m.s[cfsSeqTypes].addSimpleStruct(m, name = "", baseType = ""):
|
||||
m.s[cfsSeqTypes].add(extract(m.g.nimtv))
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv != "":
|
||||
if m.g.nimtv.buf.len != 0:
|
||||
let externc = if m.config.backend == backendCpp or
|
||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
||||
else: ""
|
||||
m.s[cfsProcs].addf(
|
||||
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
||||
[externc.rope])
|
||||
sfCompileToCpp in m.module.flags: ExternC
|
||||
else: None
|
||||
m.s[cfsProcs].addDeclWithVisibility(externc):
|
||||
m.s[cfsProcs].addProcHeader("NimThreadVarsSize", NimInt, cProcParams())
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].addReturn(cCast(NimInt, cSizeof("NimThreadVars")))
|
||||
|
||||
@@ -16,13 +16,16 @@ type
|
||||
p: BProc
|
||||
visitorFrmt: string
|
||||
|
||||
const
|
||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
proc genCaseRange(p: BProc, branch: PNode, info: var SwitchCaseBuilder)
|
||||
proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
|
||||
|
||||
proc visit(p: BProc, data, visitor: Snippet) =
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisit"),
|
||||
cCast(CPointer, data),
|
||||
visitor)
|
||||
|
||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
typ: PType) =
|
||||
if n == nil: return
|
||||
@@ -37,29 +40,32 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if disc.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n", [])
|
||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
lineF(p, cpsStmts, "} $n", [])
|
||||
let discField = dotField(accessor, disc.loc.snippet)
|
||||
p.s(cpsStmts).addSwitchStmt(discField):
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
var caseBuilder: SwitchCaseBuilder
|
||||
p.s(cpsStmts).addSwitchCase(caseBuilder):
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch, caseBuilder)
|
||||
else:
|
||||
p.s(cpsStmts).addCaseElse(caseBuilder)
|
||||
do:
|
||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
||||
p.s(cpsStmts).addBreak()
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
|
||||
genTraverseProc(c, dotField(accessor, field.loc.snippet), field.loc.t)
|
||||
else: internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
|
||||
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
||||
if not m.compileToCpp:
|
||||
result = "$1.Sup" % [accessor]
|
||||
result = dotField(accessor, "Sup")
|
||||
else:
|
||||
result = accessor
|
||||
|
||||
@@ -76,16 +82,14 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
let arraySize = lengthOrd(c.p.config, typ.indexType)
|
||||
var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, arraySize])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
|
||||
if p.s(cpsStmts).len == oldLen:
|
||||
var oldLen, newLen: int
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), cIntValue(arraySize)):
|
||||
oldLen = p.s(cpsStmts).buf.len
|
||||
genTraverseProc(c, subscript(accessor, i.snippet), typ.elementType)
|
||||
newLen = p.s(cpsStmts).buf.len
|
||||
if oldLen == newLen:
|
||||
# do not emit dummy long loops for faster debug builds:
|
||||
p.s(cpsStmts) = oldCode
|
||||
else:
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
of tyObject:
|
||||
var x = typ.baseClass
|
||||
if x != nil: x = x.skipTypes(skipPtrs)
|
||||
@@ -94,23 +98,25 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
|
||||
genTraverseProc(c, dotField(accessor, "Field" & $i), a)
|
||||
of tyRef:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
of tySequence:
|
||||
if optSeqDestructors notin c.p.module.config.globalOptions:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
elif containsGarbageCollectedRef(typ.elementType):
|
||||
# destructor based seqs are themselves not traced but their data is, if
|
||||
# they contain a GC'ed type:
|
||||
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCvisitSeq"),
|
||||
cCast(CPointer, accessor),
|
||||
c.visitorFrmt)
|
||||
#genTraverseProcSeq(c, accessor, typ)
|
||||
of tyString:
|
||||
if tfHasAsgn notin typ.flags:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
||||
visit(p, accessor, c.visitorFrmt)
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
lineCg(p, cpsStmts, visitorFrmt, [ropecg(c.p.module, "$1.ClE_0", [accessor]), c.visitorFrmt])
|
||||
visit(p, dotField(accessor, "ClE_0"), c.visitorFrmt)
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -119,18 +125,17 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
assert typ.kind == tySequence
|
||||
var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
var oldLen, newLen: int
|
||||
var a = TLoc(snippet: accessor)
|
||||
let le = lenExpr(c.p, a)
|
||||
|
||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.snippet, lenExpr(c.p, a)])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
|
||||
if p.s(cpsStmts).len == oldLen:
|
||||
p.s(cpsStmts).addForRangeExclusive(i.snippet, cIntValue(0), le):
|
||||
oldLen = p.s(cpsStmts).buf.len
|
||||
genTraverseProc(c, subscript(dataField(c.p, accessor), i.snippet), typ.elementType)
|
||||
newLen = p.s(cpsStmts).buf.len
|
||||
if newLen == oldLen:
|
||||
# do not emit dummy long loops for faster debug builds:
|
||||
p.s(cpsStmts) = oldCode
|
||||
else:
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
|
||||
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
var p = newProc(nil, m)
|
||||
@@ -139,11 +144,10 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
hcrOn = m.hcrOn
|
||||
typ = origTyp.skipTypes(abstractInstOwned)
|
||||
markerName = if hcrOn: result & "_actual" else: result
|
||||
header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
|
||||
t = getTypeDesc(m, typ)
|
||||
|
||||
lineF(p, cpsLocals, "$1 a;$n", [t])
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
||||
p.s(cpsLocals).addVar(kind = Local, name = "a", typ = t)
|
||||
p.s(cpsInit).addAssignment("a", cCast(t, "p"))
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
|
||||
@@ -157,18 +161,40 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".rope, typ.elementType)
|
||||
else:
|
||||
genTraverseProc(c, "(*a)".rope, typ.elementType)
|
||||
genTraverseProc(c, cDeref("a"), typ.elementType)
|
||||
|
||||
let generatedProc = "$1 {$n$2$3$4}\n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
var headerBuilder = newBuilder("")
|
||||
headerBuilder.addProcHeaderWithParams(ccNimCall, markerName, CVoid):
|
||||
var paramBuilder: ProcParamBuilder
|
||||
headerBuilder.addProcParams(paramBuilder):
|
||||
headerBuilder.addParam(paramBuilder, name = "p", typ = CPointer)
|
||||
headerBuilder.addParam(paramBuilder, name = "op", typ = NimInt)
|
||||
let header = extract(headerBuilder)
|
||||
|
||||
m.s[cfsProcHeaders].addf("$1;\n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcHeaders].add(header)
|
||||
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
||||
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcs].add(header)
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
||||
|
||||
if hcrOn:
|
||||
m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
|
||||
m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
|
||||
[result, markerName, getModuleDllPath(m)])
|
||||
var desc = newBuilder("")
|
||||
var unnamedParamBuilder: ProcParamBuilder
|
||||
desc.addProcParams(unnamedParamBuilder):
|
||||
desc.addUnnamedParam(unnamedParamBuilder, CPointer)
|
||||
desc.addUnnamedParam(unnamedParamBuilder, NimInt)
|
||||
let unnamedParams = extract(desc)
|
||||
m.s[cfsProcHeaders].addProcVar(ccNimCall, result, unnamedParams, CVoid)
|
||||
m.s[cfsDynLibInit].addAssignmentWithValue(result):
|
||||
m.s[cfsDynLibInit].addCast(procPtrTypeUnnamed(ccNimCall, CVoid, unnamedParams)):
|
||||
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
|
||||
getModuleDllPath(m),
|
||||
'"' & result & '"',
|
||||
cCast(CPointer, markerName))
|
||||
|
||||
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
discard genTypeInfoV1(m, s.loc.t, info)
|
||||
@@ -179,17 +205,28 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
||||
|
||||
if sfThread in s.flags and emulatedThreadVars(m.config):
|
||||
accessThreadLocalVar(p, s)
|
||||
sLoc = "NimTV_->" & sLoc
|
||||
sLoc = derefField("NimTV_", sLoc)
|
||||
|
||||
var c = TTraversalClosure(p: p,
|
||||
visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
|
||||
visitorFrmt: cIntValue(0) # "#nimGCvisit((void*)$1, 0);$n"
|
||||
)
|
||||
|
||||
let header = "static N_NIMCALL(void, $1)(void)" % [result]
|
||||
genTraverseProc(c, sLoc, s.loc.t)
|
||||
|
||||
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||
var headerBuilder = newBuilder("")
|
||||
headerBuilder.addProcHeaderWithParams(ccNimCall, result, CVoid):
|
||||
var paramBuilder: ProcParamBuilder
|
||||
headerBuilder.addProcParams(paramBuilder):
|
||||
# (void)
|
||||
discard
|
||||
let header = extract(headerBuilder)
|
||||
|
||||
m.s[cfsProcHeaders].addf("$1;$n", [header])
|
||||
m.s[cfsProcs].add(generatedProc)
|
||||
m.s[cfsProcHeaders].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcHeaders].add(header)
|
||||
m.s[cfsProcHeaders].finishProcHeaderAsProto()
|
||||
m.s[cfsProcs].addDeclWithVisibility(StaticProc):
|
||||
m.s[cfsProcs].add(header)
|
||||
m.s[cfsProcs].finishProcHeaderWithBody():
|
||||
m.s[cfsProcs].add(extract(p.s(cpsLocals)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsInit)))
|
||||
m.s[cfsProcs].add(extract(p.s(cpsStmts)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1436
compiler/cgen.nim
1436
compiler/cgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
ndi, lineinfos, pathutils, modulegraphs
|
||||
lineinfos, pathutils, modulegraphs, cbuilderbase
|
||||
|
||||
import std/[intsets, tables, sets]
|
||||
|
||||
@@ -43,12 +43,12 @@ type
|
||||
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
|
||||
ctCString
|
||||
TCFileSections* = array[TCFileSection, Rope] # represents a generated C file
|
||||
TCFileSections* = array[TCFileSection, Builder] # represents a generated C file
|
||||
TCProcSection* = enum # the sections a generated C proc consists of
|
||||
cpsLocals, # section of local variables for C proc
|
||||
cpsInit, # section for init of variables for C proc
|
||||
cpsStmts # section of local statements for C proc
|
||||
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
||||
TCProcSections* = array[TCProcSection, Builder] # represents a generated C proc
|
||||
BModule* = ref TCGen
|
||||
BProc* = ref TCProc
|
||||
TBlock* = object
|
||||
@@ -88,7 +88,7 @@ type
|
||||
options*: TOptions # options that should be used for code
|
||||
# generation; this is the same as prc.options
|
||||
# unless prc == nil
|
||||
optionsStack*: seq[TOptions]
|
||||
optionsStack*: seq[(TOptions, TNoteKinds)]
|
||||
module*: BModule # used to prevent excessive parameter passing
|
||||
withinLoop*: int # > 0 if we are within a loop
|
||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||
@@ -115,7 +115,7 @@ type
|
||||
# computing alive data on our own.
|
||||
|
||||
BModuleList* = ref object of RootObj
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Builder
|
||||
mapping*: Rope # the generated mapping file (if requested)
|
||||
modules*: seq[BModule] # list of all compiled modules
|
||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
@@ -127,7 +127,7 @@ type
|
||||
graph*: ModuleGraph
|
||||
strVersion*, seqVersion*: int # version of the string/seq implementation to use
|
||||
|
||||
nimtv*: Rope # Nim thread vars; the struct body
|
||||
nimtv*: Builder # Nim thread vars; the struct body
|
||||
nimtvDeps*: seq[PType] # type deps: every module needs whole struct
|
||||
nimtvDeclared*: IntSet # so that every var/field exists only once
|
||||
# in the struct
|
||||
@@ -161,18 +161,18 @@ type
|
||||
typeInfoMarkerV2*: TypeCache
|
||||
initProc*: BProc # code for init procedure
|
||||
preInitProc*: BProc # code executed before the init proc
|
||||
hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
|
||||
hcrCreateTypeInfosProc*: Builder # type info globals are in here when HCR=on
|
||||
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
|
||||
hcrInitGuard*: IfBuilder
|
||||
typeStack*: TTypeSeq # used for type generation
|
||||
dataCache*: TNodeTable
|
||||
typeNodes*, nimTypes*: int # used for type info generation
|
||||
typeNodesName*, nimTypesName*: Rope # used for type info generation
|
||||
labels*: Natural # for generating unique module-scope names
|
||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||
extensionLoaders*: array['0'..'9', Builder] # special procs for the
|
||||
# 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
|
||||
@@ -182,18 +182,18 @@ proc includeHeader*(this: BModule; header: string) =
|
||||
if not this.headerFiles.contains header:
|
||||
this.headerFiles.add header
|
||||
|
||||
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
proc s*(p: BProc, s: TCProcSection): var Builder {.inline.} =
|
||||
# section in the current block
|
||||
result = p.blocks[^1].sections[s]
|
||||
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
proc procSec*(p: BProc, s: TCProcSection): var Builder {.inline.} =
|
||||
# top level proc sections
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
proc initBlock*(): TBlock =
|
||||
result = TBlock()
|
||||
for i in low(result.sections)..high(result.sections):
|
||||
result.sections[i] = newRopeAppender()
|
||||
result.sections[i] = newBuilder("")
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result = BProc(
|
||||
|
||||
@@ -55,7 +55,7 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||
# replace ordinary method by dispatcher method:
|
||||
let disp = getDispatcher(result[0].sym)
|
||||
if disp != nil:
|
||||
result[0].typ = disp.typ
|
||||
result[0].typ() = disp.typ
|
||||
result[0].sym = disp
|
||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||
for i in 1..<result.len:
|
||||
|
||||
@@ -311,12 +311,12 @@ proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
let curExc = ctx.newCurExcAccess()
|
||||
curExc.info = info
|
||||
let nilnode = newNode(nkNilLit)
|
||||
nilnode.typ = curExc.typ
|
||||
nilnode.typ() = curExc.typ
|
||||
result = newTree(nkAsgn, curExc, nilnode)
|
||||
|
||||
proc newOr(g: ModuleGraph, a, b: PNode): PNode {.inline.} =
|
||||
result = newTree(nkCall, newSymNode(g.getSysMagic(a.info, "or", mOr)), a, b)
|
||||
result.typ = g.getSysType(a.info, tyBool)
|
||||
result.typ() = g.getSysType(a.info, tyBool)
|
||||
result.info = a.info
|
||||
|
||||
proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
|
||||
@@ -334,7 +334,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
|
||||
newSymNode(g.getSysMagic(c.info, "of", mOf)),
|
||||
g.callCodegenProc("getCurrentException"),
|
||||
c[i])
|
||||
nextCond.typ = ctx.g.getSysType(c.info, tyBool)
|
||||
nextCond.typ() = ctx.g.getSysType(c.info, tyBool)
|
||||
nextCond.info = c.info
|
||||
|
||||
if cond.isNil:
|
||||
@@ -444,11 +444,11 @@ proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode =
|
||||
|
||||
proc newNotCall(g: ModuleGraph; e: PNode): PNode =
|
||||
result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
|
||||
result.typ = g.getSysType(e.info, tyBool)
|
||||
result.typ() = g.getSysType(e.info, tyBool)
|
||||
|
||||
proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
|
||||
result = newIntLit(g, info, ord value)
|
||||
result.typ = getSysType(g, info, tyBool)
|
||||
result.typ() = getSysType(g, info, tyBool)
|
||||
|
||||
proc captureVar(c: var Ctx, s: PSym) =
|
||||
if c.varStates.getOrDefault(s.itemId) != localRequiresLifting:
|
||||
@@ -486,7 +486,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
if n.typ.isNil: internalError(ctx.g.config, "lowerStmtListExprs: constr typ.isNil")
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
|
||||
for i in 0..<n.len:
|
||||
case n[i].kind
|
||||
@@ -513,7 +513,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
let isExpr = not isEmptyType(n.typ)
|
||||
if isExpr:
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
tmp = ctx.newTempVar(n.typ, result)
|
||||
else:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
@@ -578,7 +578,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
if isExpr:
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
let tmp = ctx.newTempVar(n.typ, result)
|
||||
|
||||
n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
|
||||
@@ -609,7 +609,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
if isExpr:
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
let tmp = ctx.newTempVar(n.typ, result)
|
||||
|
||||
if n[0].kind == nkStmtListExpr:
|
||||
@@ -646,7 +646,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
|
||||
if isExpr:
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
else:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
@@ -732,7 +732,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
if ns:
|
||||
needsSplit = true
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
let (st, ex) = exprToStmtList(n[^1])
|
||||
result.add(st)
|
||||
n[^1] = ex
|
||||
@@ -802,7 +802,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
if ns:
|
||||
needsSplit = true
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
let (st, ex) = exprToStmtList(n[0])
|
||||
result.add(st)
|
||||
n[0] = ex
|
||||
@@ -814,10 +814,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
if ns:
|
||||
needsSplit = true
|
||||
result = newNodeI(nkStmtListExpr, n.info)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
let (st, ex) = exprToStmtList(n[1])
|
||||
n.transitionSonsKind(nkBlockStmt)
|
||||
n.typ = nil
|
||||
n.typ() = nil
|
||||
n[1] = st
|
||||
result.add(n)
|
||||
result.add(ex)
|
||||
@@ -838,9 +838,9 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
# raise
|
||||
let curExc = ctx.newCurExcAccess()
|
||||
let nilnode = newNode(nkNilLit)
|
||||
nilnode.typ = curExc.typ
|
||||
nilnode.typ() = curExc.typ
|
||||
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
|
||||
cmp.typ = ctx.g.getSysType(info, tyBool)
|
||||
cmp.typ() = ctx.g.getSysType(info, tyBool)
|
||||
|
||||
let retStmt =
|
||||
if ctx.nearestFinally == 0:
|
||||
@@ -1185,11 +1185,11 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: P
|
||||
|
||||
proc createExceptionTable(ctx: var Ctx): PNode {.inline.} =
|
||||
result = newNodeI(nkBracket, ctx.fn.info)
|
||||
result.typ = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
|
||||
result.typ() = ctx.g.newArrayType(ctx.exceptionTable.len, ctx.g.getSysType(ctx.fn.info, tyInt16), ctx.idgen, ctx.fn)
|
||||
|
||||
for i in ctx.exceptionTable:
|
||||
let elem = newIntNode(nkIntLit, i)
|
||||
elem.typ = ctx.g.getSysType(ctx.fn.info, tyInt16)
|
||||
elem.typ() = ctx.g.getSysType(ctx.fn.info, tyInt16)
|
||||
result.add(elem)
|
||||
|
||||
proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||
@@ -1212,7 +1212,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||
let getNextState = newTree(nkBracketExpr,
|
||||
ctx.createExceptionTable(),
|
||||
ctx.newStateAccess())
|
||||
getNextState.typ = intTyp
|
||||
getNextState.typ() = intTyp
|
||||
|
||||
# :state = exceptionTable[:state]
|
||||
result.add(ctx.newStateAssgn(getNextState))
|
||||
@@ -1223,7 +1223,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||
ctx.g.getSysMagic(info, "==", mEqI).newSymNode(),
|
||||
ctx.newStateAccess(),
|
||||
newIntTypeNode(0, intTyp))
|
||||
cond.typ = boolTyp
|
||||
cond.typ() = boolTyp
|
||||
|
||||
let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
|
||||
let ifBranch = newTree(nkElifBranch, cond, raiseStmt)
|
||||
@@ -1236,7 +1236,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||
ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
|
||||
newIntTypeNode(0, intTyp),
|
||||
ctx.newStateAccess())
|
||||
cond.typ = boolTyp
|
||||
cond.typ() = boolTyp
|
||||
|
||||
let asgn = newTree(nkAsgn, ctx.newUnrollFinallyAccess(info), cond)
|
||||
result.add(asgn)
|
||||
@@ -1247,12 +1247,12 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||
ctx.g.getSysMagic(info, "<", mLtI).newSymNode,
|
||||
ctx.newStateAccess(),
|
||||
newIntTypeNode(0, intTyp))
|
||||
cond.typ = boolTyp
|
||||
cond.typ() = boolTyp
|
||||
|
||||
let negateState = newTree(nkCall,
|
||||
ctx.g.getSysMagic(info, "-", mUnaryMinusI).newSymNode,
|
||||
ctx.newStateAccess())
|
||||
negateState.typ = intTyp
|
||||
negateState.typ() = intTyp
|
||||
|
||||
let ifBranch = newTree(nkElifBranch, cond, ctx.newStateAssgn(negateState))
|
||||
let ifStmt = newTree(nkIfStmt, ifBranch)
|
||||
|
||||
@@ -24,7 +24,7 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
|
||||
bootSwitch(usedGoGC, defined(gogc), "--gc:go")
|
||||
bootSwitch(usedNoGC, defined(nogc), "--gc:none")
|
||||
|
||||
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
|
||||
import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
|
||||
import
|
||||
msgs, options, nversion, condsyms, extccomp, platform,
|
||||
wordrecg, nimblecmd, lineinfos, pathutils
|
||||
@@ -248,6 +248,7 @@ const
|
||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
|
||||
|
||||
template warningOptionNoop(switch: string) =
|
||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||
@@ -303,6 +304,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
else:
|
||||
result = false
|
||||
localError(conf, info, errInvalidExceptionSystem % arg)
|
||||
of "experimental":
|
||||
try:
|
||||
result = conf.features.contains parseEnum[Feature](arg)
|
||||
except ValueError:
|
||||
result = false
|
||||
localError(conf, info, errInvalidFeatureButXFound % arg)
|
||||
else:
|
||||
result = false
|
||||
invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
|
||||
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
|
||||
import std/intsets
|
||||
|
||||
@@ -88,6 +88,13 @@ 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 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, a: PType; m: var MatchCon): bool =
|
||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
@@ -117,9 +124,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
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):
|
||||
@@ -151,7 +156,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.
|
||||
@@ -177,15 +181,20 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
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:
|
||||
result = matchType(c, last(f), a, 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
|
||||
result = matchKids(c, f, ak, m)
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
@@ -309,7 +318,7 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
|
||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
||||
@@ -328,16 +337,16 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; i
|
||||
dest = existingBinding(m, dest)
|
||||
if dest == nil or dest.kind != tyGenericParam: break
|
||||
if dest != nil:
|
||||
bindings.idTablePut(a, dest)
|
||||
bindings.put(a, dest)
|
||||
when logBindings: echo "A bind ", a, " ", dest
|
||||
else:
|
||||
bindings.idTablePut(a, b)
|
||||
bindings.put(a, b)
|
||||
when logBindings: echo "B bind ", a, " ", b
|
||||
# we have a match, so bind 'arg' itself to 'concpt':
|
||||
bindings.idTablePut(concpt, arg)
|
||||
bindings.put(concpt, arg)
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
|
||||
# bind even more generic parameters
|
||||
assert invocation.kind == tyGenericInvocation
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
bindings.idTablePut(invocation[i], arg[i])
|
||||
bindings.put(invocation[i], arg[i])
|
||||
|
||||
@@ -159,11 +159,17 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimUseStrictDefs")
|
||||
defineSymbol("nimUseStrictDefs") # deadcode
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
defineSymbol("nimHasWarnStdPrefix")
|
||||
|
||||
defineSymbol("nimHasVtables")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
defineSymbol("nimHasDefaultFloatRoundtrip")
|
||||
defineSymbol("nimHasXorSet")
|
||||
|
||||
defineSymbol("nimHasLegacyNoStrictDefs")
|
||||
|
||||
@@ -115,7 +115,7 @@ proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, s
|
||||
|
||||
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
|
||||
let invalid = rstast.FileIndex(-1)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, invalid)
|
||||
if result == invalid:
|
||||
let fname = toFullPath(d.conf, fileIndex)
|
||||
result = addFilename(d.sharedState, fname)
|
||||
@@ -830,8 +830,8 @@ proc getName(n: PNode): string =
|
||||
of nkAccQuoted:
|
||||
result = "`"
|
||||
for i in 0..<n.len: result.add(getName(n[i]))
|
||||
result = "`"
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
result.add('`')
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getName(n[0])
|
||||
else:
|
||||
result = ""
|
||||
@@ -849,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
|
||||
var r = ""
|
||||
for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
|
||||
result = getIdent(cache, r)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getNameIdent(cache, n[0])
|
||||
else:
|
||||
result = nil
|
||||
@@ -863,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
|
||||
of nkAccQuoted:
|
||||
result = getRstName(n[0])
|
||||
for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = getRstName(n[0])
|
||||
else:
|
||||
result = nil
|
||||
@@ -1206,7 +1206,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
|
||||
var param = %{"name": %($genericParam)}
|
||||
if genericParam.sym.typ.len > 0:
|
||||
param["types"] = newJArray()
|
||||
param["types"].add %($genericParam.sym.typ.elementType)
|
||||
param["types"] = %($genericParam.sym.typ.elementType)
|
||||
result.json["signature"]["genericParams"].add param
|
||||
if optGenIndex in d.conf.globalOptions:
|
||||
genItem(d, n, nameNode, k, kForceExport)
|
||||
@@ -1320,7 +1320,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
|
||||
if t.startsWith("ref "): t = substr(t, 4)
|
||||
effects[i] = newIdentNode(getIdent(cache, t), n.info)
|
||||
# set the type so that the following analysis doesn't screw up:
|
||||
effects[i].typ = real[i].typ
|
||||
effects[i].typ() = real[i].typ
|
||||
|
||||
result = newTreeI(nkExprColonExpr, n.info,
|
||||
newIdentNode(getIdent(cache, $effectType), n.info), effects)
|
||||
|
||||
@@ -275,7 +275,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
# the nkPar node:
|
||||
if n.isNil:
|
||||
result = newNode(nkTupleConstr)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
if typ.n.isNil:
|
||||
internalError(conf, "cannot unpack unnamed tuple")
|
||||
unpackObjectAdd(conf, x, typ.n, result)
|
||||
@@ -298,7 +298,7 @@ proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
if n.isNil:
|
||||
result = newNode(nkBracket)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
newSeq(result.sons, lengthOrd(conf, typ).toInt)
|
||||
else:
|
||||
result = n
|
||||
@@ -319,7 +319,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template aw(k, v, field: untyped): untyped =
|
||||
if n.isNil:
|
||||
result = newNode(k)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
else:
|
||||
# check we have the right field:
|
||||
result = n
|
||||
@@ -333,12 +333,12 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
template setNil() =
|
||||
if n.isNil:
|
||||
result = newNode(nkNilLit)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
else:
|
||||
reset n[]
|
||||
result = n
|
||||
result[] = TNode(kind: nkNilLit)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
|
||||
template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
|
||||
@@ -427,7 +427,7 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
# cast through a pointer needs a new inner object:
|
||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
||||
else: x.copyTree
|
||||
y.typ = x.typ
|
||||
y.typ() = x.typ
|
||||
result = unpack(conf, a, destTyp, y)
|
||||
dealloc a
|
||||
|
||||
@@ -481,7 +481,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
if aTyp.isNil:
|
||||
internalAssert conf, i+1 < fntyp.len
|
||||
aTyp = fntyp[i+1]
|
||||
args[i+start].typ = aTyp
|
||||
args[i+start].typ() = aTyp
|
||||
sig[i] = mapType(conf, aTyp)
|
||||
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
|
||||
|
||||
|
||||
@@ -33,6 +33,19 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
let x = param
|
||||
if x.kind == nkArgList:
|
||||
for y in items(x): result.add(y)
|
||||
elif nfDefaultRefsParam in x.flags:
|
||||
# value of default param needs to be evaluated like template body
|
||||
# if it contains other template params
|
||||
var res: PNode
|
||||
if isAtom(x):
|
||||
res = newNodeI(nkPar, x.info)
|
||||
evalTemplateAux(x, actual, c, res)
|
||||
if res.len == 1: res = res[0]
|
||||
else:
|
||||
res = copyNode(x)
|
||||
for i in 0..<x.safeLen:
|
||||
evalTemplateAux(x[i], actual, c, res)
|
||||
result.add res
|
||||
else:
|
||||
result.add copyTree(x)
|
||||
|
||||
@@ -51,7 +64,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
if x == nil:
|
||||
x = copySym(s, c.idgen)
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
x.owner = nil # c.genSymOwner
|
||||
setOwner(x, nil) # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
@@ -169,7 +182,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
|
||||
d.add newSymNode(sym, info)
|
||||
result.add d
|
||||
result.add res
|
||||
result.typ = res.typ
|
||||
result.typ() = res.typ
|
||||
|
||||
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
conf: ConfigRef;
|
||||
|
||||
@@ -125,7 +125,7 @@ proc expandDefault(t: PType; info: TLineInfo): PNode =
|
||||
of tyString:
|
||||
result = newZero(t, info, nkStrLit)
|
||||
of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
|
||||
tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass,
|
||||
tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
|
||||
tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
|
||||
tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
|
||||
result = newZero(t, info, nkEmpty) # bug indicator
|
||||
|
||||
@@ -63,7 +63,7 @@ type
|
||||
|
||||
# Configuration settings for various compilers.
|
||||
# When adding new compilers, the cmake sources could be a good reference:
|
||||
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
# https://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
|
||||
template compiler(name, settings: untyped): untyped =
|
||||
proc name: TInfoCC {.compileTime.} = settings
|
||||
@@ -162,7 +162,11 @@ compiler vcc:
|
||||
linkerExe: "cl",
|
||||
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
|
||||
includeCmd: " /I",
|
||||
linkDirCmd: " /LIBPATH:",
|
||||
# HACK: we call `cl` so we have to pass `/link` for linker options,
|
||||
# but users may still want to pass arguments to `cl` (see #14221)
|
||||
# to deal with this, we add `/link` before each `/LIBPATH`,
|
||||
# the linker ignores extra `/link`s since it's an unrecognized argument
|
||||
linkDirCmd: " /link /LIBPATH:",
|
||||
linkLibCmd: " $1.lib",
|
||||
debug: " /RTC1 /Z7 ",
|
||||
pic: "",
|
||||
@@ -777,7 +781,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
# https://blog.molecular-matters.com/2017/05/09/deleting-pdb-files-locked-by-visual-studio/
|
||||
# and a bit about the .pdb format in case that is ever needed:
|
||||
# https://github.com/crosire/blink
|
||||
# http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
|
||||
# https://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles
|
||||
if conf.hcrOn and isVSCompatible(conf):
|
||||
let t = now()
|
||||
let pdb = output.string & "." & format(t, "MMMM-yyyy-HH-mm-") & $t.nanosecond & ".pdb"
|
||||
@@ -1000,10 +1004,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
|
||||
# works out of the box with `hashMainCompilationParams`.
|
||||
result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
|
||||
|
||||
const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
|
||||
const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
|
||||
type BuildCache = object
|
||||
cacheVersion: string
|
||||
outputFile: string
|
||||
outputLastModificationTime: string
|
||||
compile: seq[(string, string)]
|
||||
link: seq[string]
|
||||
linkcmd: string
|
||||
@@ -1047,6 +1052,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
bcache.nimexe = hashNimExe()
|
||||
if fileExists(bcache.outputFile):
|
||||
bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
|
||||
@@ -1067,6 +1074,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
|
||||
# xxx optimize by returning false if stdin input was the same
|
||||
for (file, hash) in bcache.depfiles:
|
||||
if $secureHashFile(file) != hash: return true
|
||||
if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
|
||||
return true
|
||||
|
||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
var bcache: BuildCache = default(BuildCache)
|
||||
@@ -1083,7 +1092,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
"jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
|
||||
[outputCurrent, output, jsonFile.string])
|
||||
var cmds: TStringSeq = default(TStringSeq)
|
||||
var prettyCmds: TStringSeq= default(TStringSeq)
|
||||
var prettyCmds: TStringSeq = default(TStringSeq)
|
||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||
for (name, cmd) in bcache.compile:
|
||||
cmds.add cmd
|
||||
|
||||
@@ -1104,7 +1104,7 @@ proc settype(n: PNode): PType =
|
||||
|
||||
proc buildOf(it, loc: PNode; o: Operators): PNode =
|
||||
var s = newNodeI(nkCurly, it.info, it.len-1)
|
||||
s.typ = settype(loc)
|
||||
s.typ() = settype(loc)
|
||||
for i in 0..<it.len-1: s[i] = it[i]
|
||||
result = newNodeI(nkCall, it.info, 3)
|
||||
result[0] = newSymNode(o.opContains)
|
||||
@@ -1165,8 +1165,12 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
result = copyTree(check)
|
||||
if access.kind == nkDotExpr:
|
||||
# change the access to the discriminator field access
|
||||
var a = copyTree(access)
|
||||
# set field name to discriminator field name
|
||||
a[1] = check[2]
|
||||
# set discriminator field type: important for `neg`
|
||||
a.typ() = check[2].typ
|
||||
result[2] = a
|
||||
# 'access.kind != nkDotExpr' can happen for object constructors
|
||||
# which we don't check yet
|
||||
|
||||
@@ -837,8 +837,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
|
||||
of nkSym:
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
|
||||
if result.typ == nil and nfOpenSym notin result.flags:
|
||||
result.typ = result.sym.typ
|
||||
if result.typ == nil:
|
||||
result.typ() = result.sym.typ
|
||||
of externIntLit:
|
||||
result.intVal = g[thisModule].fromDisk.numbers[n.litId]
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
@@ -851,8 +851,8 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
assert n2.kind == nkNone
|
||||
transitionNoneToSym(result)
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
|
||||
if result.typ == nil and nfOpenSym notin result.flags:
|
||||
result.typ = result.sym.typ
|
||||
if result.typ == nil:
|
||||
result.typ() = result.sym.typ
|
||||
else:
|
||||
for n0 in sonsReadonly(tree, n):
|
||||
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
|
||||
@@ -942,7 +942,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
result.guard = loadSym(c, g, si, s.guard)
|
||||
result.bitsize = s.bitsize
|
||||
result.alignment = s.alignment
|
||||
result.owner = loadSym(c, g, si, s.owner)
|
||||
setOwner(result, loadSym(c, g, si, s.owner))
|
||||
let externalName = g[si].fromDisk.strings[s.externalName]
|
||||
if externalName != "":
|
||||
result.loc.snippet = externalName
|
||||
@@ -998,7 +998,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
t: PackedType; si, item: int32; result: PType) =
|
||||
result.sym = loadSym(c, g, si, t.sym)
|
||||
result.owner = loadSym(c, g, si, t.owner)
|
||||
setOwner(result, loadSym(c, g, si, t.owner))
|
||||
when false:
|
||||
for op, item in pairs t.attachedOps:
|
||||
result.attachedOps[op] = loadSym(c, g, si, item)
|
||||
@@ -1062,7 +1062,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
|
||||
name: getIdent(cache, splitFile(filename).name),
|
||||
info: newLineInfo(fileIdx, 1, 1),
|
||||
position: int(fileIdx))
|
||||
m.module.owner = getPackage(conf, cache, fileIdx)
|
||||
setOwner(m.module, getPackage(conf, cache, fileIdx))
|
||||
m.module.flags = m.fromDisk.moduleFlags
|
||||
|
||||
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
|
||||
@@ -246,7 +246,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
c.unusedImports.add((result, n.info))
|
||||
let moduleIdent = if n.kind == nkInfix: n[^1] else: n
|
||||
c.unusedImports.add((result, moduleIdent.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
|
||||
@@ -317,8 +317,9 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
]#
|
||||
result = dest.kind != nkSym
|
||||
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
|
||||
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
|
||||
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(c.graph, t):
|
||||
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
|
||||
@@ -331,7 +332,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
|
||||
else:
|
||||
let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest)
|
||||
xenv.typ = getSysType(c.graph, dest.info, tyPointer)
|
||||
xenv.typ() = getSysType(c.graph, dest.info, tyPointer)
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
|
||||
|
||||
proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
|
||||
@@ -407,7 +408,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
result = newNodeI(nkCall, info)
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault)))
|
||||
result.typ = t
|
||||
result.typ() = t
|
||||
|
||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
@@ -464,7 +465,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
c.finishCopy(newCall, n, {}, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
@@ -473,7 +474,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
c.finishCopy(m, n, {}, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
@@ -501,7 +502,7 @@ proc containsConstSeq(n: PNode): bool =
|
||||
return true
|
||||
result = false
|
||||
case n.kind
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
|
||||
result = containsConstSeq(n[1])
|
||||
of nkObjConstr, nkClosure:
|
||||
for i in 1..<n.len:
|
||||
@@ -761,7 +762,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
let tmp = c.getTemp(s, n[0].typ, n.info)
|
||||
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
|
||||
m.add p(n[0], c, s, normal)
|
||||
c.finishCopy(m, n[0], isFromSink = false)
|
||||
c.finishCopy(m, n[0], {}, isFromSink = false)
|
||||
result = newTree(nkStmtList, c.genWasMoved(tmp), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
@@ -821,14 +822,17 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ = n.typ
|
||||
n[1].typ() = n.typ
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
result[1].typ = nTyp
|
||||
result[1].typ() = nTyp
|
||||
else:
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
elif n.kind in {nkObjDownConv, nkObjUpConv}:
|
||||
result = copyTree(n)
|
||||
result[0] = p(n[0], c, s, sinkArg)
|
||||
elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
|
||||
result = copyTree(n)
|
||||
result[1] = p(n[1], c, s, sinkArg)
|
||||
elif n.typ == nil:
|
||||
# 'raise X' can be part of a 'case' expression. Deal with it here:
|
||||
result = p(n, c, s, normal)
|
||||
@@ -894,7 +898,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
elif c.inSpawn > 0:
|
||||
c.inSpawn.dec
|
||||
|
||||
let parameters = n[0].typ
|
||||
# bug #23907; skips tyGenericInst for generic callbacks
|
||||
let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
|
||||
let L = if parameters != nil: parameters.signatureLen else: 0
|
||||
|
||||
when false:
|
||||
@@ -1017,9 +1022,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned:
|
||||
# allow conversions from owned to unowned via this little hack:
|
||||
let nTyp = n[1].typ
|
||||
n[1].typ = n.typ
|
||||
n[1].typ() = n.typ
|
||||
result[1] = p(n[1], c, s, mode)
|
||||
result[1].typ = nTyp
|
||||
result[1].typ() = nTyp
|
||||
else:
|
||||
result[1] = p(n[1], c, s, mode)
|
||||
|
||||
@@ -1169,7 +1174,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
@@ -1177,7 +1182,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
@@ -1198,7 +1203,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
@@ -1218,7 +1223,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
c.finishCopy(result, dest, flags, isFromSink = false)
|
||||
|
||||
when false:
|
||||
proc computeUninit(c: var Con) =
|
||||
|
||||
@@ -33,7 +33,7 @@ import
|
||||
nversion, msgs, idents, types,
|
||||
ropes, wordrecg, renderer,
|
||||
cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
|
||||
transf, injectdestructors, sourcemap, astmsgs, backendpragmas,
|
||||
transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
|
||||
mangleutils
|
||||
|
||||
import pipelineutils
|
||||
@@ -103,7 +103,7 @@ type
|
||||
prc: PSym
|
||||
globals, locals, body: Rope
|
||||
options: TOptions
|
||||
optionsStack: seq[TOptions]
|
||||
optionsStack: seq[(TOptions, TNoteKinds)]
|
||||
module: BModule
|
||||
g: PGlobals
|
||||
beforeRetNeeded: bool
|
||||
@@ -195,7 +195,7 @@ proc mapType(typ: PType): TJSTypeKind =
|
||||
of tyPointer:
|
||||
# treat a tyPointer like a typed pointer to an array of bytes
|
||||
result = etyBaseIndex
|
||||
of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
|
||||
of tyRange, tyDistinct, tyOrdinal, tyError, tyLent:
|
||||
# tyLent is no-op as JS has pass-by-reference semantics
|
||||
result = mapType(skipModifier t)
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
|
||||
@@ -611,6 +611,17 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
r.res = frmt % [a, tmp]
|
||||
r.kind = resExpr
|
||||
|
||||
proc genBreakState(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes = default(TCompRes)
|
||||
# mangle `:state` properly somehow
|
||||
if n.kind == nkClosure:
|
||||
gen(p, n[1], a)
|
||||
r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)]
|
||||
else:
|
||||
gen(p, n, a)
|
||||
r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)]
|
||||
r.kind = resExpr
|
||||
|
||||
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
var
|
||||
x, y: TCompRes = default(TCompRes)
|
||||
@@ -1098,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
|
||||
lineF(p, "break;$n", [])
|
||||
of nkElse:
|
||||
if transferRange:
|
||||
lineF(p, "else{$n", [])
|
||||
if n.len == 2: # a dangling else for a case statement
|
||||
transferRange = false
|
||||
lineF(p, "switch ($1) {$n", [cond.rdLoc])
|
||||
lineF(p, "default: $n", [])
|
||||
else:
|
||||
lineF(p, "else{$n", [])
|
||||
else:
|
||||
lineF(p, "default: $n", [])
|
||||
p.nested:
|
||||
gen(p, it[0], stmt)
|
||||
moveInto(p, stmt, r)
|
||||
if transferRange:
|
||||
lineF(p, "}$n", [])
|
||||
lineF(p, "}$n", [])
|
||||
else:
|
||||
lineF(p, "break;$n", [])
|
||||
else: internalError(p.config, it.info, "jsgen.genCaseStmt")
|
||||
@@ -1569,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:
|
||||
@@ -2442,6 +2451,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
|
||||
of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
|
||||
of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
|
||||
of mXorSet: binaryExpr(p, n, r, "SetXor", "SetXor($1, $2)")
|
||||
of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true")
|
||||
of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]")
|
||||
of mInSet:
|
||||
@@ -2632,7 +2642,13 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
|
||||
let src = skipTypes(n[0].typ, abstractVarRange)
|
||||
let dest = skipTypes(n.typ, abstractVarRange)
|
||||
if optRangeCheck notin p.options:
|
||||
return
|
||||
if optJsBigInt64 in p.config.globalOptions and
|
||||
dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and
|
||||
src.kind in {tyInt64, tyUInt64}:
|
||||
# conversions to Number are kept
|
||||
r.res = "Number($1)" % [r.res]
|
||||
else:
|
||||
discard
|
||||
elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
|
||||
@@ -2813,9 +2829,9 @@ proc genPragma(p: PProc, n: PNode) =
|
||||
case whichPragma(it)
|
||||
of wEmit: genAsmOrEmitStmt(p, it[1])
|
||||
of wPush:
|
||||
processPushBackendOption(p.optionsStack, p.options, n, i+1)
|
||||
processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
|
||||
of wPop:
|
||||
processPopBackendOption(p.optionsStack, p.options)
|
||||
processPopBackendOption(p.config, p.optionsStack, p.options)
|
||||
else: discard
|
||||
|
||||
proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
@@ -3043,16 +3059,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkGotoState, nkState:
|
||||
globalError(p.config, n.info, "not implemented")
|
||||
of nkBreakState:
|
||||
var a: TCompRes = default(TCompRes)
|
||||
if n[0].kind == nkClosure:
|
||||
gen(p, n[0][1], a)
|
||||
let sym = n[0][1].typ[0].n[0].sym
|
||||
r.res = "(($1).$2 < 0)" % [rdLoc(a), mangleName(p.module, sym)]
|
||||
else:
|
||||
gen(p, n[0], a)
|
||||
let sym = n[0].typ[0].n[0].sym
|
||||
r.res = "((($1.ClE_0).$2) < 0)" % [rdLoc(a), mangleName(p.module, sym)]
|
||||
r.kind = resExpr
|
||||
genBreakState(p, n[0], r)
|
||||
of nkPragmaBlock: gen(p, n.lastSon, r)
|
||||
of nkComesFrom:
|
||||
discard "XXX to implement for better stack traces"
|
||||
|
||||
@@ -175,6 +175,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
|
||||
#echo "produced environment: ", param.id, " for ", routine.id
|
||||
|
||||
proc getEnvParam*(routine: PSym): PSym =
|
||||
if routine.ast.isNil: return nil
|
||||
let params = routine.ast[paramsPos]
|
||||
let hidden = lastSon(params)
|
||||
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
|
||||
@@ -283,8 +284,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
||||
" Consider using a <ref $2> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
" Consider using a <ref T> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
|
||||
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
|
||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||
[s.name.s, owner.name.s, $owner.typ.callConv])
|
||||
@@ -419,6 +420,12 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
|
||||
#echo "adding closure to ", fn.name.s
|
||||
|
||||
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
|
||||
let cp = getEnvParam(iter)
|
||||
doAssert(cp != nil, "Env param not present in iter")
|
||||
let upField = lookupInRecord(cp.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
|
||||
upField != nil
|
||||
|
||||
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
@@ -437,7 +444,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
let body = transformBody(c.graph, c.idgen, s, {useCache})
|
||||
detectCapturedVars(body, s, c)
|
||||
let ow = s.skipGenericOwner
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and (not s.isIterator or iterEnvHasUpField(c.graph, s))
|
||||
let interested = interestingVar(s)
|
||||
if ow == owner:
|
||||
if owner.isIterator:
|
||||
@@ -602,7 +609,7 @@ proc rawClosureCreation(owner: PSym;
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let env2 = copyTree(env)
|
||||
env2.typ = unowned.typ
|
||||
env2.typ() = unowned.typ
|
||||
result.add newAsgnStmt(unowned, env2, env.info)
|
||||
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
|
||||
|
||||
@@ -642,16 +649,27 @@ proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
|
||||
res.add newAsgnStmt(unowned, nilLit, info)
|
||||
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
|
||||
|
||||
proc closureCreationForIter(iter: PNode;
|
||||
proc getUpForIter(g: ModuleGraph; owner, iterOwner: PSym, expectedUpTyp: PType): PNode =
|
||||
var p = getHiddenParam(g, owner)
|
||||
var res = p.newSymNode
|
||||
while res.typ.skipTypes({tyOwned, tyRef, tyPtr}) != expectedUpTyp:
|
||||
let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(g.cache, upName))
|
||||
if upField == nil:
|
||||
return nil
|
||||
p = upField
|
||||
res = rawIndirectAccess(res, upField, p.info)
|
||||
res
|
||||
|
||||
proc closureCreationForIter(owner: PSym, iter: PNode;
|
||||
d: var DetectionPass; c: var LiftingPass): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
|
||||
let iterOwner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
if owner.isIterator:
|
||||
let it = getHiddenParam(d.graph, owner)
|
||||
if iterOwner.isIterator:
|
||||
let it = getHiddenParam(d.graph, iterOwner)
|
||||
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
|
||||
vnode = indirectAccess(newSymNode(it), v, v.info)
|
||||
else:
|
||||
@@ -660,12 +678,14 @@ proc closureCreationForIter(iter: PNode;
|
||||
addVar(vs, vnode)
|
||||
result.add(vs)
|
||||
result.add genCreateEnv(vnode)
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, iterOwner)
|
||||
|
||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
||||
if upField != nil:
|
||||
let u = setupEnvVar(owner, d, c, iter.info)
|
||||
if u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == upField.typ.skipTypes({tyOwned, tyRef, tyPtr}):
|
||||
let expectedUpTyp = upField.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
let u = if iterOwner == owner: setupEnvVar(iterOwner, d, c, iter.info)
|
||||
else: getUpForIter(d.graph, owner, iterOwner, expectedUpTyp)
|
||||
if u != nil and u.typ.skipTypes({tyOwned, tyRef, tyPtr}) == expectedUpTyp:
|
||||
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
|
||||
u, iter.info))
|
||||
else:
|
||||
@@ -699,7 +719,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
let available = getHiddenParam(d.graph, owner)
|
||||
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
|
||||
elif s.isIterator:
|
||||
result = closureCreationForIter(n, d, c)
|
||||
result = closureCreationForIter(owner, n, d, c)
|
||||
elif s.skipGenericOwner == owner:
|
||||
# direct dependency, so use the outer's env variable:
|
||||
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
|
||||
@@ -766,7 +786,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
let oldInContainer = c.inContainer
|
||||
c.inContainer = 0
|
||||
let m = newSymNode(n[namePos].sym)
|
||||
m.typ = n.typ
|
||||
m.typ() = n.typ
|
||||
result = liftCapturedVars(m, owner, d, c)
|
||||
c.inContainer = oldInContainer
|
||||
of nkHiddenStdConv:
|
||||
|
||||
82
compiler/layeredtable.nim
Normal file
82
compiler/layeredtable.nim
Normal file
@@ -0,0 +1,82 @@
|
||||
import std/tables
|
||||
import ast
|
||||
|
||||
type
|
||||
LayeredIdTableObj* {.acyclic.} = object
|
||||
## stack of type binding contexts implemented as a linked list
|
||||
topLayer*: TypeMapping
|
||||
## the mappings on the current layer
|
||||
nextLayer*: ref LayeredIdTableObj
|
||||
## the parent type binding context, possibly `nil`
|
||||
previousLen*: int
|
||||
## total length of the bindings up to the parent layer,
|
||||
## used to track if new bindings were added
|
||||
|
||||
const useRef = not defined(gcDestructors)
|
||||
# implementation detail, only arc/orc doesn't cause issues when
|
||||
# using LayeredIdTable as an object and not a ref
|
||||
|
||||
when useRef:
|
||||
type LayeredIdTable* = ref LayeredIdTableObj
|
||||
else:
|
||||
type LayeredIdTable* = LayeredIdTableObj
|
||||
|
||||
proc initLayeredTypeMap*(pt: sink TypeMapping = initTypeMapping()): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: pt, nextLayer: nil)
|
||||
|
||||
proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} =
|
||||
## copies only the type bindings of the current layer, but not any parent layers,
|
||||
## useful for write-only bindings
|
||||
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen)
|
||||
|
||||
proc currentLen*(pt: LayeredIdTable): int =
|
||||
## the sum of the cached total binding count of the parents and
|
||||
## the current binding count, just used to track if bindings were added
|
||||
pt.previousLen + pt.topLayer.len
|
||||
|
||||
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen)
|
||||
when useRef:
|
||||
result.nextLayer = pt
|
||||
else:
|
||||
new(result.nextLayer)
|
||||
result.nextLayer[] = pt
|
||||
|
||||
proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
|
||||
when useRef:
|
||||
pt = pt.nextLayer
|
||||
else:
|
||||
when defined(gcDestructors):
|
||||
pt = pt.nextLayer[]
|
||||
else:
|
||||
# workaround refc
|
||||
let tmp = pt.nextLayer[]
|
||||
pt = tmp
|
||||
|
||||
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
|
||||
result = nil
|
||||
var tm = typeMap
|
||||
while tm != nil:
|
||||
result = getOrDefault(tm.topLayer, key)
|
||||
if result != nil: return
|
||||
tm = tm.nextLayer
|
||||
|
||||
template lookup*(typeMap: ref LayeredIdTableObj, key: PType): PType =
|
||||
## recursively looks up binding of `key` in all parent layers
|
||||
lookup(typeMap, key.itemId)
|
||||
|
||||
when not useRef:
|
||||
proc lookup(typeMap: LayeredIdTableObj, key: ItemId): PType {.inline.} =
|
||||
result = getOrDefault(typeMap.topLayer, key)
|
||||
if result == nil and typeMap.nextLayer != nil:
|
||||
result = lookup(typeMap.nextLayer, key)
|
||||
|
||||
template lookup*(typeMap: LayeredIdTableObj, key: PType): PType =
|
||||
lookup(typeMap, key.itemId)
|
||||
|
||||
proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} =
|
||||
typeMap.topLayer[key] = value
|
||||
|
||||
template put*(typeMap: var LayeredIdTable, key, value: PType) =
|
||||
## binds `key` to `value` only in current layer
|
||||
put(typeMap, key.itemId, value)
|
||||
@@ -49,7 +49,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result = newNodeI(nkBracketExpr, a.info, 2)
|
||||
result[0] = a
|
||||
result[1] = i
|
||||
result.typ = elemType
|
||||
result.typ() = elemType
|
||||
|
||||
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
@@ -68,7 +68,7 @@ proc dotField(x: PNode, f: PSym): PNode =
|
||||
else:
|
||||
result[0] = x
|
||||
result[1] = newSymNode(f, x.info)
|
||||
result.typ = f.typ
|
||||
result.typ() = f.typ
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
result = newNodeI(nkAsgn, le.info, 2)
|
||||
@@ -88,7 +88,7 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
elif c.kind == attachedDestructor and c.addMemReset:
|
||||
let call = genBuiltin(c, mDefault, "default", x)
|
||||
call.typ = t
|
||||
call.typ() = t
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
@@ -105,7 +105,7 @@ proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c, mLtI, "<", i)
|
||||
cmp.add genLen(c.g, dest)
|
||||
cmp.typ = getSysType(c.g, c.info, tyBool)
|
||||
cmp.typ() = getSysType(c.g, c.info, tyBool)
|
||||
result[0] = cmp
|
||||
result[1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
@@ -127,10 +127,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
|
||||
dotExpr.add newSymNode(field)
|
||||
|
||||
let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr)
|
||||
offsetOf.typ = intType
|
||||
offsetOf.typ() = intType
|
||||
|
||||
let minusExpr = genBuiltin(c, mSubI, "-", castExpr1)
|
||||
minusExpr.typ = intType
|
||||
minusExpr.typ() = intType
|
||||
minusExpr.add offsetOf
|
||||
|
||||
let objPtr = makePtrType(objType.owner, objType, c.idgen)
|
||||
@@ -224,10 +224,16 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.baseClass != nil:
|
||||
let obj = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
obj.add newNodeI(nkEmpty, c.info)
|
||||
obj.add x
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, obj, y)
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
@@ -259,7 +265,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
# because the wasMoved(dest) call would zero out src, if dest aliases src.
|
||||
var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = x.typ
|
||||
@@ -290,7 +296,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
|
||||
proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
|
||||
result = newIntLit(g, info, ord value)
|
||||
result.typ = getSysType(g, info, tyBool)
|
||||
result.typ() = getSysType(g, info, tyBool)
|
||||
|
||||
proc getCycleParam(c: TLiftCtx): PNode =
|
||||
assert c.kind in {attachedAsgn, attachedDup}
|
||||
@@ -539,18 +545,18 @@ proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
# don't call genAddr(c, x) here:
|
||||
result = genBuiltin(c, mNewSeq, "newSeq", x)
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
result.add lenCall
|
||||
|
||||
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthStr, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||
result.add lenCall
|
||||
|
||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
lenCall.typ() = getSysType(c.g, x.info, tyInt)
|
||||
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
|
||||
op = instantiateGeneric(c, op, t, t)
|
||||
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
|
||||
@@ -573,7 +579,7 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
)
|
||||
cond.typ = getSysType(c.g, c.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, c.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
|
||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
@@ -714,7 +720,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
alignOf.typ() = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr))
|
||||
@@ -724,7 +730,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
||||
@@ -732,7 +738,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -759,7 +765,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic:
|
||||
if isFinal(elemType):
|
||||
let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
|
||||
typInfo.typ = getSysType(c.g, c.info, tyPointer)
|
||||
typInfo.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y)
|
||||
else:
|
||||
# If the ref is polymorphic we have to account for this
|
||||
@@ -780,7 +786,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Closures are really like refs except they always use a virtual destructor
|
||||
## and we need to do the refcounting only on the ref field which we call 'xenv':
|
||||
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
xenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||
let tmp =
|
||||
@@ -796,7 +802,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isCyclic: "nimDecRefIsLastCyclicDyn"
|
||||
else: "nimDecRefIsLast"
|
||||
let cond = callCodegenProc(c.g, decRefProc, c.info, tmp)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
cond.typ() = getSysType(c.g, x.info, tyBool)
|
||||
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
@@ -808,7 +814,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
body.add newAsgnStmt(x, y)
|
||||
@@ -820,7 +826,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yenv.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
@@ -872,7 +878,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
||||
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
|
||||
alignOf.typ = getSysType(c.g, c.info, tyInt)
|
||||
alignOf.typ() = getSysType(c.g, c.info, tyInt)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
||||
@@ -895,14 +901,14 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# a big problem is that we don't know the environment's type here, so we
|
||||
# have to go through some indirection; we delegate this to the codegen:
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
call.typ = t
|
||||
call.typ() = t
|
||||
call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy))
|
||||
call[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
# we 'nil' y out afterwards so we *need* to take over its reference
|
||||
@@ -911,13 +917,13 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
of attachedDestructor:
|
||||
@@ -932,7 +938,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
|
||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
|
||||
@@ -1040,7 +1046,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
else:
|
||||
discard "cannot copy openArray"
|
||||
|
||||
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
||||
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
|
||||
tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
|
||||
@@ -1146,8 +1152,8 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
||||
let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ = xx.typ
|
||||
xx.typ() = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ() = xx.typ
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
@@ -1270,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
|
||||
|
||||
@@ -32,12 +32,12 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||
proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
|
||||
let field = addUniqueField(c.objType, s, c.cache, c.idgen)
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = c.objType
|
||||
deref.typ() = c.objType
|
||||
deref.add(newSymNode(c.partialParam, info))
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add(deref)
|
||||
result.add(newSymNode(field))
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc liftLocals(n: PNode; i: int; c: var Ctx) =
|
||||
let it = n[i]
|
||||
|
||||
@@ -92,7 +92,7 @@ type
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnStdPrefix = "StdPrefix"
|
||||
warnUser = "User",
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
@@ -198,7 +198,7 @@ const
|
||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||
warnBareExcept: "$1",
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnGenericsIgnoredInjection: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnStdPrefix: "$1 needs the 'std' prefix",
|
||||
warnUser: "$1",
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
@@ -268,6 +268,7 @@ const
|
||||
NotesVerbosity* = computeNotesVerbosity()
|
||||
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
|
||||
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
|
||||
errFloatToString* = "cannot convert '$1' to '$2'"
|
||||
|
||||
type
|
||||
TFileInfo* = object
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import std/strutils
|
||||
from std/sugar import dup
|
||||
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages, modulegraphs
|
||||
export packages
|
||||
|
||||
const
|
||||
@@ -97,7 +97,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
sym.kind != skResult and # ignore `result`
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
@@ -138,7 +138,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
||||
@@ -154,5 +154,5 @@ template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragma
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -50,7 +50,7 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
case x.kind
|
||||
of nkIdent: id.add(x.ident.s)
|
||||
of nkSym: id.add(x.sym.name.s)
|
||||
of nkSymChoices:
|
||||
of nkSymChoices, nkOpenSym:
|
||||
if x[0].kind == nkSym:
|
||||
id.add(x[0].sym.name.s)
|
||||
else:
|
||||
@@ -63,6 +63,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkOpenSym:
|
||||
result = considerQuotedIdent(c, n[0], origin)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
|
||||
@@ -561,23 +563,33 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
var amb: bool = false
|
||||
discard errorUseQualifier(c, info, s, amb)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
|
||||
result = ""
|
||||
for i in 0 ..< indent:
|
||||
result.add(' ')
|
||||
result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
var i = 0
|
||||
for candidate in candidates:
|
||||
if i == 0: err.add " -- $1 the following:\n" % prefix
|
||||
else: err.add "\n"
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ)
|
||||
if i == 0: result.add " -- $1 the following:\n" % prefix
|
||||
else: result.add "\n"
|
||||
for i in 0 ..< indent:
|
||||
result.add(' ')
|
||||
result.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
result.add ": " & typeToString(candidate.typ)
|
||||
inc i
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
|
||||
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
|
||||
|
||||
proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
|
||||
var candidates = newSeq[PSym](choices.len)
|
||||
let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
|
||||
for i, n in choices:
|
||||
candidates[i] = n.sym
|
||||
errorUseQualifier(c, info, candidates, prefix)
|
||||
result = ambiguousIdentifierMsg(candidates, prefix, indent)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
||||
localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
|
||||
|
||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
||||
var err: string
|
||||
@@ -630,9 +642,10 @@ type
|
||||
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
|
||||
includePureEnum = false): seq[PSym] =
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
if result.len == 0:
|
||||
if skEnumField in filter and (result.len == 0 or includePureEnum):
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
@@ -665,24 +678,33 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
c.isAmbiguous = amb
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkOpenSym:
|
||||
result = qualifiedLookUp(c, n[0], flags)
|
||||
of nkDotExpr:
|
||||
result = nil
|
||||
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
|
||||
if m != nil and m.kind == skModule:
|
||||
var ident: PIdent = nil
|
||||
if n[1].kind == nkIdent:
|
||||
ident = n[1].ident
|
||||
elif n[1].kind == nkAccQuoted:
|
||||
if n[1].kind == nkAccQuoted:
|
||||
ident = considerQuotedIdent(c, n[1])
|
||||
else:
|
||||
# this includes sym and symchoice nodes, but since we are looking in
|
||||
# a module, it shouldn't matter what was captured
|
||||
ident = n[1].getPIdent
|
||||
if ident != nil:
|
||||
if m == c.module:
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
result = initIdentIter(ti, c.topLevelScope.symbols, ident)
|
||||
if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
|
||||
# another symbol exists with same name
|
||||
c.isAmbiguous = true
|
||||
else:
|
||||
var amb: bool = false
|
||||
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
|
||||
var amb: bool = false
|
||||
result = errorUseQualifier(c, n.info, m, amb)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident)
|
||||
result = someSymAmb(c.graph, m, ident, amb)
|
||||
if amb: c.isAmbiguous = true
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
|
||||
elif n[1].kind == nkSym:
|
||||
@@ -701,6 +723,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
if result != nil and result.kind == skStub: loadStub(result)
|
||||
|
||||
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`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
case n.kind
|
||||
|
||||
@@ -174,12 +174,12 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||
# returns a[].field as a node
|
||||
assert field.kind == skField
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst)[0]
|
||||
deref.typ() = a.typ.skipTypes(abstractInst)[0]
|
||||
deref.add a
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
# returns a.field as a node
|
||||
@@ -187,7 +187,7 @@ proc rawDirectAccess*(obj, field: PSym): PNode =
|
||||
result = newNodeI(nkDotExpr, field.info)
|
||||
result.add newSymNode(obj)
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc lookupInRecord(n: PNode, id: ItemId): PSym =
|
||||
result = nil
|
||||
@@ -250,12 +250,12 @@ proc newDotExpr*(obj, b: PSym): PNode =
|
||||
assert field != nil, b.name.s
|
||||
result.add newSymNode(obj)
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst).elementType
|
||||
deref.typ() = a.typ.skipTypes(abstractInst).elementType
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
while true:
|
||||
@@ -273,12 +273,12 @@ proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst).elementType
|
||||
deref.typ() = a.typ.skipTypes(abstractInst).elementType
|
||||
var t = deref.typ.skipTypes(abstractInst)
|
||||
var field: PSym
|
||||
let bb = getIdent(cache, b)
|
||||
@@ -297,7 +297,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
|
||||
result = newNodeI(nkDotExpr, info)
|
||||
result.add deref
|
||||
result.add newSymNode(field)
|
||||
result.typ = field.typ
|
||||
result.typ() = field.typ
|
||||
|
||||
proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
||||
assert v.kind != skField
|
||||
@@ -320,7 +320,7 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
||||
proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
|
||||
result = newNodeI(nkAddr, n.info, 1)
|
||||
result[0] = n
|
||||
result.typ = newType(typeKind, idgen, n.typ.owner)
|
||||
result.typ() = newType(typeKind, idgen, n.typ.owner)
|
||||
result.typ.rawAddSon(n.typ)
|
||||
|
||||
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
||||
@@ -344,18 +344,18 @@ proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||
if optionalArgs != nil:
|
||||
for i in 1..<optionalArgs.len-2:
|
||||
result.add optionalArgs[i]
|
||||
result.typ = sym.typ.returnType
|
||||
result.typ() = sym.typ.returnType
|
||||
|
||||
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
|
||||
result = nkIntLit.newIntNode(value)
|
||||
result.typ = getSysType(g, info, tyInt)
|
||||
result.typ() = getSysType(g, info, tyInt)
|
||||
|
||||
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
|
||||
if skipTypes(n.typ, abstractVar).kind == tyArray:
|
||||
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
result.typ() = getSysType(g, n.info, tyInt)
|
||||
result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
|
||||
result[1] = n
|
||||
|
||||
@@ -364,7 +364,7 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info, 2)
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
result.typ() = getSysType(g, n.info, tyInt)
|
||||
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
||||
result[1] = n
|
||||
|
||||
|
||||
@@ -166,4 +166,4 @@ proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
|
||||
result = n
|
||||
else:
|
||||
result = newTree(nkHiddenAddr, n)
|
||||
result.typ = makePtrType(n.typ, idgen)
|
||||
result.typ() = makePtrType(n.typ, idgen)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm]
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
|
||||
import ic / [packed_ast, ic]
|
||||
@@ -88,6 +88,7 @@ type
|
||||
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
||||
invalidTransitiveClosure: bool
|
||||
interactive*: bool
|
||||
withinSystem*: bool # in system.nim or a module imported by system.nim
|
||||
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
|
||||
# first module that included it
|
||||
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
|
||||
@@ -274,6 +275,25 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
|
||||
else:
|
||||
result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
|
||||
|
||||
proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
|
||||
let importHidden = optImportHidden in m.options
|
||||
if isCachedModule(g, m):
|
||||
result = nil
|
||||
for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
|
||||
if result == nil:
|
||||
# set result to the first symbol
|
||||
result = s
|
||||
else:
|
||||
# another symbol found
|
||||
amb = true
|
||||
break
|
||||
else:
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
|
||||
if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
|
||||
# another symbol exists with same name
|
||||
amb = true
|
||||
|
||||
proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
|
||||
result = someSym(g, g.systemModule, name)
|
||||
|
||||
@@ -452,6 +472,49 @@ proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic):
|
||||
proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
|
||||
result = createMagic(g, g.idgen, name, m)
|
||||
|
||||
proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
|
||||
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
|
||||
## so that it is useful as a C identifier snippet.
|
||||
let fid = FileIndex(m.position)
|
||||
let path = AbsoluteFile toFullPath(conf, fid)
|
||||
var isLib = false
|
||||
var rel = ""
|
||||
if path.string.startsWith(conf.libpath.string):
|
||||
isLib = true
|
||||
rel = relativeTo(path, conf.libpath).string
|
||||
else:
|
||||
rel = relativeTo(path, conf.projectPath).string
|
||||
|
||||
if not isLib and not belongsToProjectPackage(conf, m):
|
||||
# special handlings for nimble packages
|
||||
when DirSep == '\\':
|
||||
let rel2 = replace(rel, '\\', '/')
|
||||
else:
|
||||
let rel2 = rel
|
||||
const pkgs2 = "pkgs2/"
|
||||
var start = rel2.find(pkgs2)
|
||||
if start >= 0:
|
||||
start += pkgs2.len
|
||||
start += skipUntil(rel2, {'/'}, start)
|
||||
if start+1 < rel2.len:
|
||||
rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
|
||||
|
||||
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
|
||||
result = newStringOfCap(trunc)
|
||||
for i in 0..<trunc:
|
||||
let c = rel[i]
|
||||
case c
|
||||
of 'a'..'z', '0'..'9':
|
||||
result.add c
|
||||
of {os.DirSep, os.AltSep}:
|
||||
result.add 'Z' # because it looks a bit like '/'
|
||||
of '.':
|
||||
result.add 'O' # a circle
|
||||
else:
|
||||
# We mangle upper letters too so that there cannot
|
||||
# be clashes with our special meanings of 'Z' and 'O'
|
||||
result.addInt ord(c)
|
||||
|
||||
proc registerModule*(g: ModuleGraph; m: PSym) =
|
||||
assert m != nil
|
||||
assert m.kind == skModule
|
||||
@@ -463,7 +526,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
|
||||
setLen(g.packed.pm, m.position + 1)
|
||||
|
||||
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
|
||||
uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
|
||||
uniqueName: rope(uniqueModuleName(g.config, m)))
|
||||
initStrTables(g, m)
|
||||
|
||||
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
|
||||
@@ -677,8 +740,6 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
proc configComplete*(g: ModuleGraph) =
|
||||
rememberStartupConfig(g.startupPackedConfig, g.config)
|
||||
|
||||
from std/strutils import repeat, `%`
|
||||
|
||||
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
|
||||
let conf = graph.config
|
||||
let isNimscript = conf.isDefined("nimscript")
|
||||
|
||||
@@ -25,7 +25,7 @@ template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
|
||||
|
||||
proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||
let packSym = getPackage(graph, fileIdx)
|
||||
result.owner = packSym
|
||||
setOwner(result, packSym)
|
||||
result.position = int fileIdx
|
||||
|
||||
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
|
||||
@@ -629,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
|
||||
message(conf, info, warnDeprecated, msg)
|
||||
|
||||
proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
|
||||
if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
|
||||
if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
|
||||
writeContext(conf, info)
|
||||
liMessage(conf, info, errInternal, errMsg, doAbort, info2)
|
||||
|
||||
@@ -669,31 +669,6 @@ template listMsg(title, r) =
|
||||
proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
|
||||
proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
|
||||
|
||||
proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
|
||||
## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
|
||||
## so that it is useful as a C identifier snippet.
|
||||
let path = AbsoluteFile toFullPath(conf, fid)
|
||||
let rel =
|
||||
if path.string.startsWith(conf.libpath.string):
|
||||
relativeTo(path, conf.libpath).string
|
||||
else:
|
||||
relativeTo(path, conf.projectPath).string
|
||||
let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
|
||||
result = newStringOfCap(trunc)
|
||||
for i in 0..<trunc:
|
||||
let c = rel[i]
|
||||
case c
|
||||
of 'a'..'z', '0'..'9':
|
||||
result.add c
|
||||
of {os.DirSep, os.AltSep}:
|
||||
result.add 'Z' # because it looks a bit like '/'
|
||||
of '.':
|
||||
result.add 'O' # a circle
|
||||
else:
|
||||
# We mangle upper letters too so that there cannot
|
||||
# be clashes with our special meanings of 'Z' and 'O'
|
||||
result.addInt ord(c)
|
||||
|
||||
proc genSuccessX*(conf: ConfigRef) =
|
||||
let mem =
|
||||
when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
|
||||
|
||||
@@ -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
|
||||
@@ -919,7 +919,7 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
|
||||
newSymNode(op, r.info),
|
||||
l,
|
||||
r)
|
||||
result.typ = newType(tyBool, ctx.idgen, nil)
|
||||
result.typ() = newType(tyBool, ctx.idgen, nil)
|
||||
|
||||
proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
var cache = newIdentCache()
|
||||
@@ -929,7 +929,7 @@ proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
result = nkPrefix.newTree(
|
||||
newSymNode(op, node.info),
|
||||
node)
|
||||
result.typ = newType(tyBool, ctx.idgen, nil)
|
||||
result.typ() = newType(tyBool, ctx.idgen, nil)
|
||||
|
||||
proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
|
||||
infix(ctx, l, r, mEqRef)
|
||||
|
||||
@@ -4,12 +4,12 @@ hint[XDeclaredButNotUsed]:off
|
||||
|
||||
define:booting
|
||||
define:nimcore
|
||||
define:nimPreviewFloatRoundtrip
|
||||
define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
define:nimPreviewCheckedClose
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
@@ -48,7 +48,7 @@ define:useStdoutAsStdmsg
|
||||
|
||||
|
||||
@if nimUseStrictDefs:
|
||||
experimental:strictDefs
|
||||
experimental:strictDefs # deadcode
|
||||
warningAsError[Uninit]:on
|
||||
warningAsError[ProveInit]:on
|
||||
@end
|
||||
|
||||
@@ -84,7 +84,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
|
||||
elemType = settype[0]
|
||||
first = firstOrd(conf, elemType).toInt64
|
||||
result = newNodeI(nkCurly, info)
|
||||
result.typ = settype
|
||||
result.typ() = settype
|
||||
result.info = info
|
||||
e = 0
|
||||
while e < s.len * ElemSize:
|
||||
@@ -101,7 +101,7 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
|
||||
result.add aa
|
||||
else:
|
||||
n = newNodeI(nkRange, info)
|
||||
n.typ = elemType
|
||||
n.typ() = elemType
|
||||
n.add aa
|
||||
let bb = newIntTypeNode(b + first, elemType)
|
||||
bb.info = info
|
||||
|
||||
@@ -204,6 +204,7 @@ type
|
||||
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
|
||||
nkReplayAction # for .rod file support: A replay action
|
||||
nkNilRodNode # for .rod file support: a 'nil' PNode
|
||||
nkOpenSym # container for captured sym that can be overriden by local symbols
|
||||
|
||||
const
|
||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||
|
||||
@@ -179,7 +179,6 @@ const
|
||||
cmdCtags, cmdBuildindex}
|
||||
|
||||
type
|
||||
NimVer* = tuple[major: int, minor: int, patch: int]
|
||||
TStringSeq* = seq[string]
|
||||
TGCMode* = enum # the selected GC
|
||||
gcUnselected = "unselected"
|
||||
@@ -223,11 +222,14 @@ type
|
||||
strictEffects,
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictDefs, # deadcode
|
||||
strictCaseObjects,
|
||||
inferGenericTypes,
|
||||
genericsOpenSym,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
# alternative to above:
|
||||
genericsOpenSym
|
||||
vtables
|
||||
typeBoundOps
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -246,6 +248,8 @@ type
|
||||
## Useful for libraries that rely on local passC
|
||||
jsNoLambdaLifting
|
||||
## Old transformation for closures in JS backend
|
||||
noStrictDefs
|
||||
## disable "strictdefs"
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
@@ -394,8 +398,7 @@ type
|
||||
outDir*: AbsoluteDir
|
||||
jsonBuildFile*: AbsoluteFile
|
||||
prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
|
||||
nimStdlibVersion*: NimVer
|
||||
dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
|
||||
dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
|
||||
projectName*: string # holds a name like 'nim'
|
||||
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull*: AbsoluteFile # projectPath/projectName
|
||||
@@ -407,7 +410,6 @@ type
|
||||
commandArgs*: seq[string] # any arguments after the main command
|
||||
commandLine*: string
|
||||
extraCmds*: seq[string] # for writeJsonBuildInstructions
|
||||
keepComments*: bool # whether the parser needs to keep comments
|
||||
implicitImports*: seq[string] # modules that are to be implicitly imported
|
||||
implicitIncludes*: seq[string] # modules that are to be implicitly included
|
||||
docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
|
||||
@@ -448,16 +450,6 @@ type
|
||||
clientProcessId*: int
|
||||
|
||||
|
||||
proc parseNimVersion*(a: string): NimVer =
|
||||
# could be moved somewhere reusable
|
||||
result = default(NimVer)
|
||||
if a.len > 0:
|
||||
let b = a.split(".")
|
||||
assert b.len == 3, a
|
||||
template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
|
||||
fn(0)
|
||||
fn(1)
|
||||
fn(2)
|
||||
|
||||
proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
|
||||
## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
|
||||
@@ -591,7 +583,6 @@ proc newConfigRef*(): ConfigRef =
|
||||
command: "", # the main command (e.g. cc, check, scan, etc)
|
||||
commandArgs: @[], # any arguments after the main command
|
||||
commandLine: "",
|
||||
keepComments: true, # whether the parser needs to keep comments
|
||||
implicitImports: @[], # modules that are to be implicitly imported
|
||||
implicitIncludes: @[], # modules that are to be implicitly included
|
||||
docSeeSrcUrl: "",
|
||||
@@ -632,12 +623,6 @@ proc newPartialConfigRef*(): ConfigRef =
|
||||
proc cppDefine*(c: ConfigRef; define: string) =
|
||||
c.cppDefines.incl define
|
||||
|
||||
proc getStdlibVersion*(conf: ConfigRef): NimVer =
|
||||
if conf.nimStdlibVersion == (0,0,0):
|
||||
let s = conf.symbols.getOrDefault("nimVersion", "")
|
||||
conf.nimStdlibVersion = s.parseNimVersion
|
||||
result = conf.nimStdlibVersion
|
||||
|
||||
proc isDefined*(conf: ConfigRef; symbol: string): bool =
|
||||
if conf.symbols.hasKey(symbol):
|
||||
result = true
|
||||
|
||||
@@ -51,3 +51,11 @@ func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
conf.mainPackageId == sym.getPackageId
|
||||
|
||||
func belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
## Returns `false` if `sym` is nil.
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
sym != nil and conf.mainPackageId == sym.getPackageId
|
||||
|
||||
@@ -266,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
|
||||
{tyOpenArray, tyTuple, tyObject}:
|
||||
result = isAssignable(owner, n[1])
|
||||
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
|
||||
elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
|
||||
# types that are equal modulo distinction preserve l-value:
|
||||
result = isAssignable(owner, n[1])
|
||||
of nkHiddenDeref:
|
||||
|
||||
@@ -1401,7 +1401,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
|
||||
result = primarySuffix(p, result, baseInd, mode)
|
||||
|
||||
proc binaryNot(p: var Parser; a: PNode): PNode =
|
||||
if p.tok.tokType == tkNot:
|
||||
if p.tok.tokType == tkNot and p.tok.indent < 0:
|
||||
let notOpr = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
optInd(p, notOpr)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -47,9 +47,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
|
||||
of NonePass:
|
||||
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
||||
|
||||
proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
|
||||
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
|
||||
) =
|
||||
proc processImplicitImports*(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
|
||||
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator) =
|
||||
# XXX fixme this should actually be relative to the config file!
|
||||
let relativeTo = toFullPath(graph.config, m.info)
|
||||
for module in items(implicits):
|
||||
@@ -64,7 +63,7 @@ proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind
|
||||
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
|
||||
break
|
||||
|
||||
proc prePass(c: PContext; n: PNode) =
|
||||
proc prePass*(c: PContext; n: PNode) =
|
||||
for son in n:
|
||||
if son.kind == nkPragma:
|
||||
for s in son:
|
||||
|
||||
@@ -370,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
|
||||
proc processNote(c: PContext, n: PNode) =
|
||||
template handleNote(enumVals, notes) =
|
||||
let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
|
||||
if x != errUnknown:
|
||||
if x != errUnknown:
|
||||
nk = TNoteKind(x)
|
||||
let x = c.semConstBoolExpr(c, n[1])
|
||||
n[1] = x
|
||||
@@ -722,12 +722,12 @@ proc processPragma(c: PContext, n: PNode, i: int) =
|
||||
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
||||
proc processExc(c: PContext, x: PNode) =
|
||||
if c.hasUnresolvedArgs(c, x):
|
||||
x.typ = makeTypeFromExpr(c, x)
|
||||
x.typ() = makeTypeFromExpr(c, x)
|
||||
else:
|
||||
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
|
||||
if t.kind notin {tyObject, tyOr}:
|
||||
localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
|
||||
x.typ = t
|
||||
x.typ() = t
|
||||
|
||||
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||
let it = n[1]
|
||||
@@ -800,13 +800,14 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||
proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
|
||||
var callNode: PNode
|
||||
|
||||
if n.kind in {nkIdent, nkSym}:
|
||||
case n.kind
|
||||
of nkIdentKinds:
|
||||
# pragma -> pragma()
|
||||
callNode = newTree(nkCall, n)
|
||||
elif n.kind == nkExprColonExpr:
|
||||
of nkExprColonExpr:
|
||||
# pragma: arg -> pragma(arg)
|
||||
callNode = newTree(nkCall, n[0], n[1])
|
||||
elif n.kind in nkPragmaCallKinds:
|
||||
of nkPragmaCallKinds - {nkExprColonExpr}:
|
||||
callNode = n
|
||||
else:
|
||||
invalidPragma(c, n)
|
||||
@@ -1343,6 +1344,16 @@ proc mergePragmas(n, pragmas: PNode) =
|
||||
else:
|
||||
for p in pragmas: n[pragmasPos].add p
|
||||
|
||||
proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
|
||||
if n[pragmasPos].kind == nkEmpty:
|
||||
n[pragmasPos] = newNodeI(nkPragma, n.info)
|
||||
for p in pragmas:
|
||||
let prag = whichPragma(p)
|
||||
if prag in validPragmas:
|
||||
let copy = copyTree(p)
|
||||
overwriteLineInfo copy, n.info
|
||||
n[pragmasPos].add copy
|
||||
|
||||
proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
|
||||
validPragmas: TSpecialWords) =
|
||||
if sym != nil and sym.kind != skModule:
|
||||
@@ -1356,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
|
||||
internalError(c.config, info, "implicitPragmas")
|
||||
inc i
|
||||
popInfoContext(c.config)
|
||||
if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
|
||||
if sym.kind in routineKinds and sym.ast != nil:
|
||||
mergeValidPragmas(sym.ast, o, validPragmas)
|
||||
|
||||
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
|
||||
localError(c.config, info, ".dynlib requires .exportc")
|
||||
|
||||
54
compiler/pushpoppragmas.nim
Normal file
54
compiler/pushpoppragmas.nim
Normal file
@@ -0,0 +1,54 @@
|
||||
import pragmas, options, ast, trees, lineinfos, idents, wordrecg
|
||||
import std/assertions
|
||||
|
||||
import renderer
|
||||
|
||||
|
||||
proc processNote(config: ConfigRef, n: PNode) =
|
||||
template handleNote(enumVals, notes) =
|
||||
let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
|
||||
assert x != errUnknown
|
||||
assert n[1].kind == nkIntLit
|
||||
|
||||
nk = TNoteKind(x)
|
||||
if n[1].intVal != 0: incl(notes, nk)
|
||||
else: excl(notes, nk)
|
||||
|
||||
var nk: TNoteKind
|
||||
case whichKeyword(n[0][0].ident)
|
||||
of wHint: handleNote(hintMin .. hintMax, config.notes)
|
||||
of wWarning: handleNote(warnMin .. warnMax, config.notes)
|
||||
of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors)
|
||||
of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors)
|
||||
else: discard
|
||||
|
||||
proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) =
|
||||
optionsStack.add (options, notes)
|
||||
|
||||
proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
|
||||
let entry = optionsStack[^1]
|
||||
options = entry[0]
|
||||
config.notes = entry[1]
|
||||
optionsStack.setLen(optionsStack.len-1)
|
||||
|
||||
proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions,
|
||||
n: PNode, start: int) =
|
||||
pushBackendOption(optionsStack, options, config.notes)
|
||||
for i in start..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind in nkPragmaCallKinds and it.len == 2:
|
||||
if it[0].kind == nkBracketExpr and
|
||||
it[0].len == 2 and
|
||||
it[0][1].kind == nkIdent and it[0][0].kind == nkIdent:
|
||||
processNote(config, it)
|
||||
elif it[1].kind == nkIntLit:
|
||||
let sw = whichPragma(it[0])
|
||||
let opts = pragmaToOptions(sw)
|
||||
if opts != {}:
|
||||
if it[1].intVal != 0:
|
||||
options.incl opts
|
||||
else:
|
||||
options.excl opts
|
||||
|
||||
template processPopBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
|
||||
popBackendOption(config, optionsStack, options)
|
||||
@@ -442,6 +442,11 @@ proc atom(g: TSrcGen; n: PNode): string =
|
||||
result = $n.floatVal & "\'f64"
|
||||
else:
|
||||
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
|
||||
of nkFloat128Lit:
|
||||
if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
|
||||
result = $n.floatVal & "\'f128"
|
||||
else:
|
||||
result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
|
||||
of nkNilLit: result = "nil"
|
||||
of nkType:
|
||||
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
|
||||
@@ -514,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
if n.len > 0: result += lsub(g, n[0])
|
||||
of nkOpenSym: result = lsub(g, n[0])
|
||||
of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
|
||||
of nkTupleClassTy: result = len("tuple")
|
||||
of nkDotExpr: result = lsons(g, n) + 1
|
||||
@@ -1013,7 +1019,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
|
||||
proc skipHiddenNodes(n: PNode): PNode =
|
||||
result = n
|
||||
while result != nil:
|
||||
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
|
||||
if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
|
||||
result = result[1]
|
||||
elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
|
||||
result.len > 0:
|
||||
@@ -1275,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
|
||||
else:
|
||||
gsub(g, n, 0)
|
||||
of nkOpenSym: gsub(g, n, 0)
|
||||
of nkPar, nkClosure:
|
||||
put(g, tkParLe, "(")
|
||||
gcomma(g, n, c)
|
||||
@@ -1304,10 +1311,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
put(g, tkCustomLit, n[0].strVal)
|
||||
gsub(g, n, 1)
|
||||
else:
|
||||
gsub(g, n, 0)
|
||||
for i in 0..<n.len-1:
|
||||
gsub(g, n, i)
|
||||
put(g, tkDot, ".")
|
||||
assert n.len == 2, $n.len
|
||||
accentedName(g, n[1])
|
||||
if n.len > 1:
|
||||
accentedName(g, n[^1])
|
||||
of nkBind:
|
||||
putWithSpace(g, tkBind, "bind")
|
||||
gsub(g, n, 0)
|
||||
|
||||
@@ -322,6 +322,14 @@ proc intersects(s1, s2: IntSet): bool =
|
||||
if s2.contains(a):
|
||||
return true
|
||||
|
||||
proc hasPushOrPopPragma(n: DepN): bool =
|
||||
# Checks if the tree node has some pragmas that do not
|
||||
# play well with reordering, like the push/pop pragma
|
||||
# no crossing for push/pop barrier
|
||||
let a = n.pnode
|
||||
result = a.kind == nkPragma and a[0].kind == nkIdent and
|
||||
(a[0].ident.s == "push" or a[0].ident.s == "pop")
|
||||
|
||||
proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
|
||||
# Build a dependency graph
|
||||
result = newSeqOfCap[DepN](deps.len)
|
||||
@@ -363,6 +371,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
|
||||
for dep in deps[i][0]:
|
||||
if dep in declares:
|
||||
ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
|
||||
elif hasPushOrPopPragma(nj):
|
||||
# Every node that comes after a push/pop pragma must
|
||||
# depend on it; vice versa
|
||||
if j < i:
|
||||
ni.kids.add nj
|
||||
else:
|
||||
nj.kids.add ni
|
||||
else:
|
||||
for d in declares:
|
||||
if uses.contains(d):
|
||||
@@ -403,18 +418,7 @@ proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
|
||||
if v.idx < 0:
|
||||
strongConnect(v, idx, s, result)
|
||||
|
||||
proc hasForbiddenPragma(n: PNode): bool =
|
||||
# Checks if the tree node has some pragmas that do not
|
||||
# play well with reordering, like the push/pop pragma
|
||||
result = false
|
||||
for a in n:
|
||||
if a.kind == nkPragma and a[0].kind == nkIdent and
|
||||
a[0].ident.s == "push":
|
||||
return true
|
||||
|
||||
proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
|
||||
if n.hasForbiddenPragma:
|
||||
return n
|
||||
var includedFiles = initIntSet()
|
||||
let mpath = toFullPath(graph.config, module.fileIdx)
|
||||
let n = expandIncludes(graph, module, n, mpath,
|
||||
|
||||
@@ -18,7 +18,7 @@ import
|
||||
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
|
||||
lowerings, plugins/active, lineinfos, int128,
|
||||
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
|
||||
extccomp
|
||||
extccomp, layeredtable
|
||||
|
||||
import vtables
|
||||
import std/[strtabs, math, tables, intsets, strutils, packedsets]
|
||||
@@ -89,6 +89,11 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
|
||||
changeType(c, x, formal, check=true)
|
||||
result = arg
|
||||
result = skipHiddenSubConv(result, c.graph, c.idgen)
|
||||
# mark inserted converter as used:
|
||||
var a = result
|
||||
if a.kind == nkHiddenDeref: a = a[0]
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
markUsed(c, a.info, a[0].sym)
|
||||
|
||||
|
||||
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
@@ -97,13 +102,13 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
renderTree(arg, {renderNoComments}))
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
result.typ() = formal
|
||||
elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
|
||||
# Pick the right 'sym' from the sym choice by looking at 'formal' type:
|
||||
result = nil
|
||||
for ch in arg:
|
||||
if sameType(ch.typ, formal):
|
||||
return getConstExpr(c.module, ch, c.idgen, c.graph)
|
||||
return ch
|
||||
typeMismatch(c.config, info, formal, arg.typ, arg)
|
||||
else:
|
||||
result = indexTypesMatch(c, formal, arg.typ, arg)
|
||||
@@ -111,7 +116,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||
typeMismatch(c.config, info, formal, arg.typ, arg)
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
result.typ() = formal
|
||||
else:
|
||||
result = fitNodePostMatch(c, formal, result)
|
||||
|
||||
@@ -251,7 +256,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
# when there is a nested proc inside a template, semtmpl
|
||||
# will assign a wrong owner during the first pass over the
|
||||
# template; we must fix it here: see #909
|
||||
result.owner = getCurrOwner(c)
|
||||
setOwner(result, getCurrOwner(c))
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
if find(result.name.s, '`') >= 0:
|
||||
@@ -465,7 +470,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
renderTree(result, {renderNoComments}))
|
||||
result = newSymNode(errorSym(c, result))
|
||||
else:
|
||||
result.typ = makeTypeDesc(c, typ)
|
||||
result.typ() = makeTypeDesc(c, typ)
|
||||
#result = symNodeFromType(c, typ, n.info)
|
||||
else:
|
||||
if s.ast[genericParamsPos] != nil and retType.isMetaType:
|
||||
@@ -473,7 +478,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
# e.g. template foo(T: typedesc): seq[T]
|
||||
# We will instantiate the return type here, because
|
||||
# we now know the supplied arguments
|
||||
var paramTypes = initTypeMapping()
|
||||
var paramTypes = initLayeredTypeMap()
|
||||
for param, value in genericParamsInMacroCall(s, call):
|
||||
var givenType = value.typ
|
||||
# the sym nodes used for the supplied generic arguments for
|
||||
@@ -481,7 +486,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
# in this case, get the type directly from the sym
|
||||
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
|
||||
givenType = value.sym.typ
|
||||
idTablePut(paramTypes, param.typ, givenType)
|
||||
put(paramTypes, param.typ, givenType)
|
||||
|
||||
retType = generateTypeInstance(c, paramTypes,
|
||||
macroResult.info, retType)
|
||||
@@ -489,7 +494,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
if retType.kind == tyVoid:
|
||||
result = semStmt(c, result, flags)
|
||||
else:
|
||||
result = semExpr(c, result, flags, expectedType)
|
||||
result = semExpr(c, result, flags)
|
||||
result = fitNode(c, retType, result, result.info)
|
||||
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
|
||||
dec(c.config.evalTemplateCounter)
|
||||
@@ -497,7 +502,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
|
||||
const
|
||||
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
|
||||
errFloatToString = "cannot convert '$1' to '$2'"
|
||||
|
||||
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
|
||||
@@ -608,7 +612,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, ch
|
||||
newNodeIT(nkType, recNode.info, asgnType)
|
||||
)
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
asgnExpr.typ = recNode.typ
|
||||
asgnExpr.typ() = recNode.typ
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
else:
|
||||
raiseAssert "unreachable"
|
||||
@@ -630,7 +634,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
|
||||
if checkDefault: # don't add defaults when checking whether a case branch has default fields
|
||||
return
|
||||
defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
|
||||
defaultValue.typ = discriminator.typ
|
||||
defaultValue.typ() = discriminator.typ
|
||||
selectedBranch = recNode.pickCaseBranchIndex defaultValue
|
||||
defaultValue.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
|
||||
@@ -643,7 +647,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
|
||||
elif recType.kind in {tyObject, tyArray, tyTuple}:
|
||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
|
||||
if asgnExpr != nil:
|
||||
asgnExpr.typ = recNode.typ
|
||||
asgnExpr.typ() = recNode.typ
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
else:
|
||||
@@ -651,41 +655,48 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault:
|
||||
|
||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
|
||||
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
|
||||
if aTypSkip.kind == tyObject:
|
||||
case aTypSkip.kind
|
||||
of tyObject:
|
||||
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
|
||||
if child.len > 0:
|
||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
|
||||
asgnExpr.typ = aTyp
|
||||
asgnExpr.typ() = aTyp
|
||||
asgnExpr.sons.add child
|
||||
result = semExpr(c, asgnExpr)
|
||||
else:
|
||||
result = nil
|
||||
elif aTypSkip.kind == tyArray:
|
||||
of tyArray:
|
||||
let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
|
||||
|
||||
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
|
||||
result.typ() = aTyp
|
||||
else:
|
||||
result = nil
|
||||
elif aTypSkip.kind == tyTuple:
|
||||
of tyTuple:
|
||||
var hasDefault = false
|
||||
if aTypSkip.n != nil:
|
||||
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
|
||||
if hasDefault and children.len > 0:
|
||||
result = newNodeI(nkTupleConstr, a.info)
|
||||
result.typ = aTyp
|
||||
result.typ() = aTyp
|
||||
result.sons.add children
|
||||
result = semExpr(c, result)
|
||||
else:
|
||||
result = nil
|
||||
else:
|
||||
result = nil
|
||||
of tyRange:
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault"):
|
||||
result = firstRange(c.config, aTypSkip)
|
||||
else:
|
||||
result = nil
|
||||
else:
|
||||
result = nil
|
||||
|
||||
@@ -722,6 +733,8 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
|
||||
result.semOverloadedCall = semOverloadedCall
|
||||
result.semInferredLambda = semInferredLambda
|
||||
result.semGenerateInstance = generateInstance
|
||||
result.instantiateOnlyProcType = instantiateOnlyProcType
|
||||
result.fitDefaultNode = fitDefaultNode
|
||||
result.semTypeNode = semTypeNode
|
||||
result.instTypeBoundOp = sigmatch.instTypeBoundOp
|
||||
result.hasUnresolvedArgs = hasUnresolvedArgs
|
||||
|
||||
@@ -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
|
||||
@@ -246,7 +307,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add(getProcHeader(c.config, err.sym, prefer))
|
||||
candidates.addDeclaredLocMaybe(c.config, err.sym)
|
||||
candidates.add("\n")
|
||||
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
|
||||
const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
|
||||
let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
|
||||
var argList = n
|
||||
if isGenericMismatch and n[0].kind == nkBracketExpr:
|
||||
argList = n[0]
|
||||
let nArg =
|
||||
if err.firstMismatch.arg < argList.len:
|
||||
argList[err.firstMismatch.arg]
|
||||
else:
|
||||
nil
|
||||
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
|
||||
if n.len > 1:
|
||||
if verboseTypeMismatch notin c.config.legacyFeatures:
|
||||
@@ -269,6 +339,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
of kMissingParam:
|
||||
candidates.add(" missing parameter: " & nameParam)
|
||||
candidates.add "\n"
|
||||
of kExtraGenericParam:
|
||||
candidates.add(" extra generic param given")
|
||||
candidates.add "\n"
|
||||
of kMissingGenericParam:
|
||||
candidates.add(" missing generic parameter: " & nameParam)
|
||||
candidates.add "\n"
|
||||
of kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
@@ -278,6 +354,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add "\n"
|
||||
of kTypeMismatch:
|
||||
doAssert nArg != nil
|
||||
if nArg.kind in nkSymChoices:
|
||||
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
doAssert wanted != nil
|
||||
@@ -292,9 +370,39 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
effectProblem(wanted, got, candidates, c)
|
||||
candidates.add "\n"
|
||||
of kGenericParamTypeMismatch:
|
||||
let pos = err.firstMismatch.arg
|
||||
doAssert n[0].kind == nkBracketExpr and pos < n[0].len
|
||||
let arg = n[0][pos]
|
||||
doAssert arg != nil
|
||||
var wanted = err.firstMismatch.formal.typ
|
||||
if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
|
||||
wanted = wanted.genericConstraint
|
||||
let got = arg.typ.skipTypes({tyTypeDesc})
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
doAssert wanted != nil
|
||||
doAssert got != nil
|
||||
candidates.add " generic parameter mismatch, expected "
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
candidates.add " but got '"
|
||||
candidates.add renderTree(arg)
|
||||
candidates.add "' of type: "
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
if nArg.kind in nkSymChoices:
|
||||
candidates.add "\n"
|
||||
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
|
||||
if got != nil and got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
if got != nil:
|
||||
effectProblem(wanted, got, candidates, c)
|
||||
candidates.add "\n"
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
else:
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
if err.firstMismatch.kind in genericParamMismatches:
|
||||
candidates.add(" in generic parameters")
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
@@ -306,9 +414,16 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
|
||||
of kExtraArg: candidates.add("\n extra argument given")
|
||||
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
|
||||
of kTypeMismatch, kVarNeeded:
|
||||
of kExtraGenericParam:
|
||||
candidates.add("\n extra generic param given")
|
||||
of kMissingGenericParam:
|
||||
candidates.add("\n missing generic parameter: " & nameParam)
|
||||
of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
var wanted = err.firstMismatch.formal.typ
|
||||
if isGenericMismatch and wanted.kind == tyGenericParam and
|
||||
wanted.genericParamHasConstraints:
|
||||
wanted = wanted.genericConstraint
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add("\n required type for " & nameParam & ": ")
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
@@ -319,8 +434,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
else:
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
let got = nArg.typ
|
||||
var got = nArg.typ
|
||||
if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
if nArg.kind in nkSymChoices:
|
||||
candidates.add "\n"
|
||||
candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
|
||||
doAssert wanted != nil
|
||||
if got != nil:
|
||||
if got.kind == tyProc and wanted.kind == tyProc:
|
||||
@@ -331,8 +450,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
candidates.add "\n"
|
||||
if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
|
||||
n.kind == nkCommand:
|
||||
if err.firstMismatch.arg == 1 and nArg != nil and
|
||||
nArg.kind == nkTupleConstr and n.kind == nkCommand:
|
||||
maybeWrongSpace = true
|
||||
for diag in err.diagnostics:
|
||||
candidates.add(diag & "\n")
|
||||
@@ -409,23 +528,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||
result.add("\n" & errExpectedPosition & "\n" & candidates)
|
||||
localError(c.config, n.info, result)
|
||||
|
||||
proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||
var errors: CandidateErrors = @[]
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
let headSymbol = n[0]
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
if symx.kind in routineKinds:
|
||||
errors.add(CandidateError(sym: symx,
|
||||
firstMismatch: MismatchInfo(),
|
||||
diagnostics: @[],
|
||||
enabled: false))
|
||||
symx = nextOverloadIter(o, c, headSymbol)
|
||||
if errors.len == 0:
|
||||
localError(c.config, n.info, "could not resolve: " & $n)
|
||||
else:
|
||||
notFoundError(c, n, errors)
|
||||
|
||||
proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
|
||||
result = ""
|
||||
if c.compilesContextId > 0:
|
||||
@@ -518,7 +620,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
if overloadsState == csEmpty and result.state == csEmpty:
|
||||
if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
|
||||
result.state = csNoMatch
|
||||
if efNoDiagnostics in flags:
|
||||
if c.inGenericContext > 0 and nfExprCall in n.flags:
|
||||
# untyped expression calls end up here, see #24099
|
||||
return
|
||||
# xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
|
||||
localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
|
||||
@@ -554,6 +657,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
getProcHeader(c.config, alt.calleeSym),
|
||||
args])
|
||||
|
||||
proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
|
||||
var errors: CandidateErrors = @[]
|
||||
let headSymbol = n[0]
|
||||
block:
|
||||
# we build a closed symchoice of all `[]` overloads for their errors,
|
||||
# except add a custom error for the magics which always match
|
||||
var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c))
|
||||
var o: TOverloadIter = default(TOverloadIter)
|
||||
var symx = initOverloadIter(o, c, headSymbol)
|
||||
while symx != nil:
|
||||
if symx.kind in routineKinds:
|
||||
if symx.magic in {mArrGet, mArrPut}:
|
||||
errors.add(CandidateError(sym: symx,
|
||||
firstMismatch: MismatchInfo(),
|
||||
diagnostics: @[],
|
||||
enabled: false))
|
||||
else:
|
||||
choice.add newSymNode(symx, headSymbol.info)
|
||||
symx = nextOverloadIter(o, c, headSymbol)
|
||||
n[0] = choice
|
||||
# copied from semOverloadedCallAnalyzeEffects, might be overkill:
|
||||
const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
|
||||
let filter =
|
||||
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
|
||||
baseFilter + {skIterator}
|
||||
else: baseFilter
|
||||
# this will add the errors:
|
||||
var r = resolveOverloads(c, n, n, filter, flags, errors, true)
|
||||
if errors.len == 0:
|
||||
localError(c.config, n.info, "could not resolve: " & $n)
|
||||
else:
|
||||
notFoundError(c, n, errors)
|
||||
|
||||
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
||||
let a = if a.kind == nkHiddenDeref: a[0] else: a
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
@@ -561,7 +697,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
||||
if s.isGenericRoutineStrict:
|
||||
let finalCallee = generateInstance(c, s, x.bindings, a.info)
|
||||
a[0].sym = finalCallee
|
||||
a[0].typ = finalCallee.typ
|
||||
a[0].typ() = finalCallee.typ
|
||||
#a.typ = finalCallee.typ.returnType
|
||||
|
||||
proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
|
||||
@@ -570,6 +706,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
|
||||
for i in 1..<n.len:
|
||||
instGenericConvertersArg(c, n[i], x)
|
||||
|
||||
proc markConvertersUsed*(c: PContext, n: PNode) =
|
||||
assert n.kind in nkCallKinds
|
||||
for i in 1..<n.len:
|
||||
var a = n[i]
|
||||
if a == nil: continue
|
||||
if a.kind == nkHiddenDeref: a = a[0]
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
markUsed(c, a.info, a[0].sym)
|
||||
|
||||
proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||
var m = newCandidate(c, f)
|
||||
result = paramTypesMatch(m, f, a, arg, nil)
|
||||
@@ -587,15 +732,15 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
||||
# This almost exactly replicates the steps taken by the compiler during
|
||||
# param matching. It performs an embarrassing amount of back-and-forth
|
||||
# type jugling, but it's the price to pay for consistency and correctness
|
||||
result.typ = generateTypeInstance(c, m.bindings, arg.info,
|
||||
result.typ() = generateTypeInstance(c, m.bindings, arg.info,
|
||||
formal.skipTypes({tyCompositeTypeClass}))
|
||||
else:
|
||||
typeMismatch(c.config, arg.info, formal, arg.typ, arg)
|
||||
# error correction:
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
result.typ() = formal
|
||||
|
||||
proc updateDefaultParams(call: PNode) =
|
||||
proc updateDefaultParams(c: PContext, call: PNode) =
|
||||
# In generic procs, the default parameter may be unique for each
|
||||
# instantiation (see tlateboundgenericparams).
|
||||
# After a call is resolved, we need to re-assign any default value
|
||||
@@ -605,8 +750,18 @@ proc updateDefaultParams(call: PNode) =
|
||||
let calleeParams = call[0].sym.typ.n
|
||||
for i in 1..<call.len:
|
||||
if nfDefaultParam in call[i].flags:
|
||||
let def = calleeParams[i].sym.ast
|
||||
let formal = calleeParams[i].sym
|
||||
let def = formal.ast
|
||||
if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
|
||||
# mirrored with sigmatch:
|
||||
if def.kind == nkEmpty:
|
||||
# The default param value is set to empty in `instantiateProcType`
|
||||
# when the type of the default expression doesn't match the type
|
||||
# of the instantiated proc param:
|
||||
pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
|
||||
typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
|
||||
popInfoContext(c.config)
|
||||
def.typ() = errorType(c)
|
||||
call[i] = def
|
||||
|
||||
proc getCallLineInfo(n: PNode): TLineInfo =
|
||||
@@ -663,7 +818,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
|
||||
if t[i] == nil or u[i] == nil: return
|
||||
stackPut(t[i], u[i])
|
||||
of tyGenericParam:
|
||||
let prebound = x.bindings.idTableGet(t)
|
||||
let prebound = x.bindings.lookup(t)
|
||||
if prebound != nil:
|
||||
continue # Skip param, already bound
|
||||
|
||||
@@ -675,7 +830,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
|
||||
discard
|
||||
# update bindings
|
||||
for i in 0 ..< flatUnbound.len():
|
||||
x.bindings.idTablePut(flatUnbound[i], flatBound[i])
|
||||
x.bindings.put(flatUnbound[i], flatBound[i])
|
||||
|
||||
proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
n: PNode, flags: TExprFlags;
|
||||
@@ -686,12 +841,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
markUsed(c, info, finalCallee)
|
||||
onUse(info, finalCallee)
|
||||
assert finalCallee.ast != nil
|
||||
if x.hasFauxMatch:
|
||||
if x.matchedErrorType:
|
||||
result = x.call
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
|
||||
result.typ = newTypeS(x.fauxMatch, c)
|
||||
if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
|
||||
if containsGenericType(result.typ):
|
||||
result.typ() = newTypeS(tyError, c)
|
||||
incl result.typ.flags, tfCheckedForDestructor
|
||||
return
|
||||
let gp = finalCallee.ast[genericParamsPos]
|
||||
if gp.isGenericParams:
|
||||
@@ -717,16 +872,18 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
# this node will be used in template substitution,
|
||||
# pretend this is an untyped node and let regular sem handle the type
|
||||
# to prevent problems where a generic parameter is treated as a value
|
||||
tn.typ = nil
|
||||
tn.typ() = nil
|
||||
x.call.add tn
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
markConvertersUsed(c, result)
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
result.typ = finalCallee.typ.returnType
|
||||
updateDefaultParams(result)
|
||||
if finalCallee.magic notin {mArrGet, mArrPut}:
|
||||
result.typ() = finalCallee.typ.returnType
|
||||
updateDefaultParams(c, result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
result = n.len >= 2 and (let t = n[1].typ;
|
||||
@@ -734,7 +891,7 @@ proc canDeref(n: PNode): bool {.inline.} =
|
||||
|
||||
proc tryDeref(n: PNode): PNode =
|
||||
result = newNodeI(nkHiddenDeref, n.info)
|
||||
result.typ = n.typ.skipTypes(abstractInst)[0]
|
||||
result.typ() = n.typ.skipTypes(abstractInst)[0]
|
||||
result.add n
|
||||
|
||||
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
@@ -751,9 +908,9 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
candidates)
|
||||
result = semResolvedCall(c, r, n, flags, expectedType)
|
||||
else:
|
||||
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
|
||||
if c.inGenericContext > 0 and c.matchedConcept == nil:
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ = makeTypeFromExpr(c, result.copyTree)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
elif efExplain notin flags:
|
||||
# repeat the overload resolution,
|
||||
# this time enabling all the diagnostic output (this should fail again)
|
||||
@@ -768,22 +925,22 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode =
|
||||
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
|
||||
result = n
|
||||
|
||||
proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErrors, doError: bool): PNode =
|
||||
if s.kind in {skTemplate, skMacro}:
|
||||
internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
|
||||
(if s.kind == skTemplate: "template" else: "macro")
|
||||
# binding has to stay 'nil' for this to work!
|
||||
var m = newCandidate(c, s, nil)
|
||||
|
||||
for i in 1..<n.len:
|
||||
let formal = s.ast[genericParamsPos][i-1].typ
|
||||
var arg = n[i].typ
|
||||
# try transforming the argument into a static one before feeding it into
|
||||
# typeRel
|
||||
if formal.kind == tyStatic and arg.kind != tyStatic:
|
||||
let evaluated = c.semTryConstExpr(c, n[i], n[i].typ)
|
||||
if evaluated != nil:
|
||||
arg = newTypeS(tyStatic, c, son = evaluated.typ)
|
||||
arg.n = evaluated
|
||||
let tm = typeRel(m, formal, arg)
|
||||
if tm in {isNone, isConvertible}: return nil
|
||||
matchGenericParams(m, n, s)
|
||||
if m.state != csMatch:
|
||||
# state is csMatch only if *all* generic params were matched,
|
||||
# including implicit parameters
|
||||
if doError:
|
||||
errors.add(CandidateError(
|
||||
sym: s,
|
||||
firstMismatch: m.firstMismatch,
|
||||
diagnostics: m.diagnostics))
|
||||
return nil
|
||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||
newInst.typ.flags.excl tfUnresolved
|
||||
let info = getCallLineInfo(n)
|
||||
@@ -802,46 +959,57 @@ proc setGenericParams(c: PContext, n, expectedParams: PNode) =
|
||||
nil
|
||||
e = semExprWithType(c, n[i], expectedType = constraint)
|
||||
if e.typ == nil:
|
||||
n[i].typ = errorType(c)
|
||||
n[i].typ() = errorType(c)
|
||||
else:
|
||||
n[i].typ = e.typ.skipTypes({tyTypeDesc})
|
||||
n[i].typ() = e.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
setGenericParams(c, n, s.ast[genericParamsPos])
|
||||
var s = s
|
||||
var a = n[0]
|
||||
var errors: CandidateErrors = @[]
|
||||
if a.kind == nkSym:
|
||||
# common case; check the only candidate has the right
|
||||
# number of generic type parameters:
|
||||
if s.ast[genericParamsPos].safeLen != n.len-1:
|
||||
let expected = s.ast[genericParamsPos].safeLen
|
||||
localError(c.config, getCallLineInfo(n), errGenerated, "cannot instantiate: '" & renderTree(n) &
|
||||
"'; got " & $(n.len-1) & " typeof(s) but expected " & $expected)
|
||||
return n
|
||||
result = explicitGenericSym(c, n, s)
|
||||
if result == nil: result = explicitGenericInstError(c, n)
|
||||
result = explicitGenericSym(c, n, s, errors, doError)
|
||||
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.
|
||||
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
|
||||
# It's good enough for now.
|
||||
result = newNodeI(a.kind, getCallLineInfo(n))
|
||||
for i in 0..<a.len:
|
||||
var candidate = a[i].sym
|
||||
if candidate.kind in {skProc, skMethod, skConverter,
|
||||
skFunc, skIterator}:
|
||||
# it suffices that the candidate has the proper number of generic
|
||||
# type parameters:
|
||||
if candidate.ast[genericParamsPos].safeLen == n.len-1:
|
||||
let x = explicitGenericSym(c, n, candidate)
|
||||
if x != nil: result.add(x)
|
||||
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 == 1 and a.kind == nkClosedSymChoice:
|
||||
result = result[0]
|
||||
elif result.len == 0: result = explicitGenericInstError(c, n)
|
||||
# candidateCount != 1: return explicitGenericInstError(c, n)
|
||||
if result.len == 0:
|
||||
result = nil
|
||||
if doError:
|
||||
notFoundError(c, n, errors)
|
||||
else:
|
||||
result = explicitGenericInstError(c, n)
|
||||
# probably unreachable: we are trying to instantiate `a` which is not
|
||||
# a sym/symchoice
|
||||
if doError:
|
||||
result = explicitGenericInstError(c, n)
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
|
||||
@@ -15,8 +15,8 @@ when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import
|
||||
options, ast, astalgo, msgs, idents, renderer,
|
||||
magicsys, vmdef, modulegraphs, lineinfos, pathutils
|
||||
options, ast, msgs, idents, renderer,
|
||||
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable
|
||||
|
||||
import ic / ic
|
||||
|
||||
@@ -73,9 +73,9 @@ type
|
||||
efNoUndeclared, efIsDotCall, efCannotBeDotCall,
|
||||
# Use this if undeclared identifiers should not raise an error during
|
||||
# overload resolution.
|
||||
efNoDiagnostics,
|
||||
efTypeAllowed # typeAllowed will be called after
|
||||
efWantNoDefaults
|
||||
efIgnoreDefaults # var statements without initialization
|
||||
efAllowSymChoice # symchoice node should not be resolved
|
||||
|
||||
TExprFlags* = set[TExprFlag]
|
||||
@@ -136,9 +136,13 @@ type
|
||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
|
||||
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
||||
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
|
||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
|
||||
semInferredLambda*: proc(c: PContext, pt: LayeredIdTable, n: PNode): PNode
|
||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
info: TLineInfo): PSym
|
||||
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
|
||||
@@ -168,6 +172,7 @@ type
|
||||
inUncheckedAssignSection*: int
|
||||
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
||||
skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
|
||||
inTypeofContext*: int
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
@@ -191,29 +196,29 @@ proc getIntLitType*(c: PContext; literal: PNode): PType =
|
||||
proc setIntLitType*(c: PContext; result: PNode) =
|
||||
let i = result.intVal
|
||||
case c.config.target.intSize
|
||||
of 8: result.typ = getIntLitType(c, result)
|
||||
of 8: result.typ() = getIntLitType(c, result)
|
||||
of 4:
|
||||
if i >= low(int32) and i <= high(int32):
|
||||
result.typ = getIntLitType(c, result)
|
||||
result.typ() = getIntLitType(c, result)
|
||||
else:
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt64)
|
||||
of 2:
|
||||
if i >= low(int16) and i <= high(int16):
|
||||
result.typ = getIntLitType(c, result)
|
||||
result.typ() = getIntLitType(c, result)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ = getSysType(c.graph, result.info, tyInt32)
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt32)
|
||||
else:
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt64)
|
||||
of 1:
|
||||
# 8 bit CPUs are insane ...
|
||||
if i >= low(int8) and i <= high(int8):
|
||||
result.typ = getIntLitType(c, result)
|
||||
result.typ() = getIntLitType(c, result)
|
||||
elif i >= low(int16) and i <= high(int16):
|
||||
result.typ = getSysType(c.graph, result.info, tyInt16)
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt16)
|
||||
elif i >= low(int32) and i <= high(int32):
|
||||
result.typ = getSysType(c.graph, result.info, tyInt32)
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt32)
|
||||
else:
|
||||
result.typ = getSysType(c.graph, result.info, tyInt64)
|
||||
result.typ() = getSysType(c.graph, result.info, tyInt64)
|
||||
else:
|
||||
internalError(c.config, result.info, "invalid int size")
|
||||
|
||||
@@ -445,7 +450,7 @@ when false:
|
||||
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStaticExpr, n.info)
|
||||
result.sons = @[n]
|
||||
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
|
||||
result.typ() = if n.typ != nil and n.typ.kind == tyStatic: n.typ
|
||||
else: newTypeS(tyStatic, c, n.typ)
|
||||
|
||||
proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||
@@ -504,7 +509,7 @@ proc errorType*(c: PContext): PType =
|
||||
|
||||
proc errorNode*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
result.typ = errorType(c)
|
||||
result.typ() = errorType(c)
|
||||
|
||||
# These mimic localError
|
||||
template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode =
|
||||
@@ -525,10 +530,11 @@ template localErrorNode*(c: PContext, n: PNode, arg: string): PNode =
|
||||
liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc())
|
||||
errorNode(c, n2)
|
||||
|
||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
dest.kind = kind
|
||||
dest.owner = getCurrOwner(c)
|
||||
dest.size = - 1
|
||||
when false:
|
||||
proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
|
||||
dest.kind = kind
|
||||
dest.owner = getCurrOwner(c)
|
||||
dest.size = - 1
|
||||
|
||||
proc makeRangeType*(c: PContext; first, last: BiggestInt;
|
||||
info: TLineInfo; intType: PType = nil): PType =
|
||||
@@ -559,7 +565,7 @@ proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
|
||||
|
||||
proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
|
||||
result = newSymNode(symFromType(c, t, info), info)
|
||||
result.typ = makeTypeDesc(c, t)
|
||||
result.typ() = makeTypeDesc(c, t)
|
||||
|
||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,15 @@ type
|
||||
replaceByFieldName: bool
|
||||
c: PContext
|
||||
|
||||
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)
|
||||
@@ -72,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)
|
||||
@@ -148,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
|
||||
|
||||
@@ -38,7 +38,7 @@ proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph):
|
||||
# original type was 'int', not a distinct int etc.
|
||||
if n.typ.kind == tyInt:
|
||||
# access cache for the int lit type
|
||||
result.typ = getIntLitTypeG(g, result, idgen)
|
||||
result.typ() = getIntLitTypeG(g, result, idgen)
|
||||
result.info = n.info
|
||||
|
||||
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
|
||||
@@ -46,12 +46,12 @@ proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
|
||||
result = newFloatNode(nkFloat32Lit, floatVal)
|
||||
else:
|
||||
result = newFloatNode(nkFloatLit, floatVal)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
result.info = n.info
|
||||
|
||||
proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
|
||||
result = newStrNode(nkStrLit, strVal)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
result.info = n.info
|
||||
|
||||
proc getConstExpr*(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
@@ -302,6 +302,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
of mMinusSet:
|
||||
result = nimsets.diffSets(g.config, a, b)
|
||||
result.info = n.info
|
||||
of mXorSet:
|
||||
result = nimsets.symdiffSets(g.config, a, b)
|
||||
result.info = n.info
|
||||
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
|
||||
of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g)
|
||||
of mRepr:
|
||||
@@ -316,7 +319,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
|
||||
of mArrToSeq:
|
||||
result = copyTree(a)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
of mCompileOption:
|
||||
result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g)
|
||||
of mCompileOptionArg:
|
||||
@@ -411,7 +414,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g)
|
||||
of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
else:
|
||||
raiseAssert $srcTyp.kind
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
@@ -420,14 +423,17 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
|
||||
of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
|
||||
var val = a.getOrdValue
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
if dstTyp.kind in {tyUInt..tyUInt64}:
|
||||
result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
|
||||
result.transitionIntKind(nkUIntLit)
|
||||
else:
|
||||
if check: rangeCheck(n, val, g)
|
||||
result = newIntNodeT(val, n, idgen, g)
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit}:
|
||||
result.typ() = n.typ
|
||||
if check and result.kind in {nkCharLit..nkUInt64Lit} and
|
||||
dstTyp.kind notin {tyUInt..tyUInt64}:
|
||||
rangeCheck(n, getInt(result), g)
|
||||
of tyFloat..tyFloat64:
|
||||
case srcTyp.kind
|
||||
@@ -435,12 +441,12 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
|
||||
result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g)
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
of tyOpenArray, tyVarargs, tyProc, tyPointer:
|
||||
result = nil
|
||||
else:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
|
||||
proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
if n.kind == nkBracket:
|
||||
@@ -511,10 +517,10 @@ proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
|
||||
result = newSymNode(s, info)
|
||||
if s.typ.kind != tyTypeDesc:
|
||||
result.typ = newType(tyTypeDesc, idgen, s.owner)
|
||||
result.typ() = newType(tyTypeDesc, idgen, s.owner)
|
||||
result.typ.addSonSkipIntLit(s.typ, idgen)
|
||||
else:
|
||||
result.typ = s.typ
|
||||
result.typ() = s.typ
|
||||
|
||||
proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
result = nil
|
||||
@@ -633,7 +639,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if s.typ.kind == tyStatic:
|
||||
if s.typ.n != nil and tfUnresolved notin s.typ.flags:
|
||||
result = s.typ.n
|
||||
result.typ = s.typ.base
|
||||
result.typ() = s.typ.base
|
||||
elif s.typ.isIntLit:
|
||||
result = s.typ.n
|
||||
else:
|
||||
@@ -746,7 +752,9 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
if a == nil: return
|
||||
if leValueConv(n[1], a) and leValueConv(a, n[2]):
|
||||
result = a # a <= x and x <= b
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
elif n.typ.kind in {tyUInt..tyUInt64}:
|
||||
discard "don't check uints"
|
||||
else:
|
||||
localError(g.config, n.info,
|
||||
"conversion from $1 to $2 is invalid" %
|
||||
@@ -755,7 +763,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
var a = getConstExpr(m, n[0], idgen, g)
|
||||
if a == nil: return
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
var a = getConstExpr(m, n[1], idgen, g)
|
||||
if a == nil: return
|
||||
@@ -769,10 +777,11 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
|
||||
var a = getConstExpr(m, n[1], idgen, g)
|
||||
if a == nil: return
|
||||
if n.typ != nil and n.typ.kind in NilableTypes and
|
||||
not (n.typ.kind == tyProc and a.typ.kind == tyProc):
|
||||
(n.typ.kind != tyProc or a.typ.kind != tyProc or
|
||||
sameBackendType(n.typ, a.typ)):
|
||||
# we allow compile-time 'cast' for pointer types:
|
||||
result = a
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g)
|
||||
of nkDotExpr: result = foldFieldAccess(m, n, idgen, g)
|
||||
of nkCheckedFieldExpr:
|
||||
|
||||
@@ -61,6 +61,7 @@ template canOpenSym(s): bool =
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var GenericCtx; flags: TSemGenericFlags,
|
||||
isAmbiguous: bool,
|
||||
fromDotExpr=false): PNode =
|
||||
result = nil
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
@@ -73,8 +74,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
if canOpenSym(s):
|
||||
result.flags.incl nfOpenSym
|
||||
if result.kind == nkSym: result.typ = nil
|
||||
if openSym in c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ() = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
@@ -98,13 +105,28 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
if s.typ != nil and s.typ.kind == tyStatic:
|
||||
if s.typ.n != nil:
|
||||
result = s.typ.n
|
||||
elif c.inGenericContext > 0 and withinConcept notin flags:
|
||||
# don't leave generic param as identifier node in generic type,
|
||||
# sigmatch will try to instantiate generic type AST without all params
|
||||
# fine to give a symbol node a generic type here since
|
||||
# we are in a generic context and `prepareNode` will be called
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
else:
|
||||
result = n
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
@@ -112,18 +134,41 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
of skType:
|
||||
if (s.typ != nil) and
|
||||
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
|
||||
if isAmbiguous:
|
||||
# ambiguous types should be symchoices since lookup behaves
|
||||
# differently for them in regular expressions
|
||||
maybeDotChoice(c, n, s, fromDotExpr)
|
||||
return
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
elif c.inGenericContext > 0 and withinConcept notin flags:
|
||||
# don't leave generic param as identifier node in generic type,
|
||||
# sigmatch will try to instantiate generic type AST without all params
|
||||
# fine to give a symbol node a generic type here since
|
||||
# we are in a generic context and `prepareNode` will be called
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
else:
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
if openSym in c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
onUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
@@ -145,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
elif s.isMixedIn:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
else:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags, amb)
|
||||
# else: leave as nkIdent
|
||||
|
||||
proc newDot(n, b: PNode): PNode =
|
||||
@@ -161,10 +206,11 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
|
||||
let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
|
||||
|
||||
c.isAmbiguous = false
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
if s != nil:
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous)
|
||||
else:
|
||||
n[0] = semGenericStmt(c, n[0], flags, ctx)
|
||||
result = n
|
||||
@@ -178,24 +224,21 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
if withinBind in flags or s.id in ctx.toBind:
|
||||
if s.kind == skType: # don't put types in sym choice
|
||||
result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true))
|
||||
var ambig = false
|
||||
if candidates.len > 1:
|
||||
let s2 = searchInScopes(c, ident, ambig)
|
||||
result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
|
||||
isAmbiguous = ambig, fromDotExpr = true))
|
||||
else:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
elif s.isMixedIn:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
var ambig = false
|
||||
if s.kind == skType and candidates.len > 1:
|
||||
var ambig = false
|
||||
let s2 = searchInScopes(c, ident, ambig)
|
||||
if ambig:
|
||||
# this is a type conversion like a.T where T is ambiguous with
|
||||
# other types or routines
|
||||
# in regular code, this never considers a type conversion and
|
||||
# skips to routine overloading
|
||||
# so symchoices are used which behave similarly with type symbols
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
return
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||
discard searchInScopes(c, ident, ambig)
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags,
|
||||
isAmbiguous = ambig, fromDotExpr = true)
|
||||
result = newDot(result, syms)
|
||||
|
||||
proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
|
||||
@@ -262,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
# check if it is an expression macro:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
let fn = n[0]
|
||||
c.isAmbiguous = false
|
||||
var s = qualifiedLookUp(c, fn, {})
|
||||
let ambig = c.isAmbiguous
|
||||
if s == nil and
|
||||
{withinMixin, withinConcept}*flags == {} and
|
||||
fn.kind in {nkIdent, nkAccQuoted} and
|
||||
@@ -315,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
of skType:
|
||||
# bad hack for generics:
|
||||
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
|
||||
result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
|
||||
if ambig:
|
||||
# ambiguous types should be symchoices since lookup behaves
|
||||
# differently for them in regular expressions
|
||||
result[0] = sc
|
||||
else:
|
||||
result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
|
||||
onUse(fn.info, s)
|
||||
first = 1
|
||||
else:
|
||||
@@ -563,9 +613,27 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
else:
|
||||
body = getBody(c.graph, s)
|
||||
else: body = n[bodyPos]
|
||||
n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
|
||||
let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
|
||||
n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
|
||||
closeScope(c)
|
||||
of nkPragma, nkPragmaExpr: discard
|
||||
of nkPragmaExpr:
|
||||
result[1] = semGenericStmt(c, n[1], flags, ctx)
|
||||
of nkPragma:
|
||||
for i in 0 ..< n.len:
|
||||
let x = n[i]
|
||||
let prag = whichPragma(x)
|
||||
if x.kind in nkPragmaCallKinds:
|
||||
# process each child individually to prevent untyped macros/templates
|
||||
# from instantiating
|
||||
# if pragma is language-level pragma, skip name node:
|
||||
let start = ord(prag != wInvalid)
|
||||
for j in start ..< x.len:
|
||||
# treat as mixin context for user pragmas & macro args
|
||||
x[j] = semGenericStmt(c, x[j], flags+{withinMixin}, ctx)
|
||||
elif prag == wInvalid:
|
||||
# only sem if not a language-level pragma
|
||||
# treat as mixin context for user pragmas & macro args
|
||||
result[i] = semGenericStmt(c, x, flags+{withinMixin}, ctx)
|
||||
of nkExprColonExpr, nkExprEqExpr:
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
result[1] = semGenericStmt(c, n[1], flags, ctx)
|
||||
|
||||
@@ -34,7 +34,7 @@ proc pushProcCon*(c: PContext; owner: PSym) =
|
||||
const
|
||||
errCannotInstantiateX = "cannot instantiate: '$1'"
|
||||
|
||||
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
|
||||
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable): PSym =
|
||||
internalAssert c.config, n.kind == nkGenericParams
|
||||
for a in n.items:
|
||||
internalAssert c.config, a.kind == nkSym
|
||||
@@ -43,7 +43,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
|
||||
s.flags.incl {sfUsed, sfFromGeneric}
|
||||
var t = idTableGet(pt, q.typ)
|
||||
var t = lookup(pt, q.typ)
|
||||
if t == nil:
|
||||
if tfRetType in q.typ.flags:
|
||||
# keep the generic type and allow the return type to be bound
|
||||
@@ -53,7 +53,9 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS
|
||||
if q.typ.kind != tyCompositeTypeClass:
|
||||
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind in {tyGenericParam, tyConcept}:
|
||||
elif t.kind in {tyGenericParam, tyConcept, tyFromExpr} or
|
||||
# generic body types are accepted as typedesc arguments
|
||||
(t.kind == tyGenericBody and q.typ.kind != tyTypeDesc):
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
|
||||
@@ -109,7 +111,7 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMappi
|
||||
elif s.owner == nil or s.owner.kind == skPackage:
|
||||
#echo "copied this ", s.name.s
|
||||
x = copySym(s, c.idgen)
|
||||
x.owner = owner
|
||||
setOwner(x, owner)
|
||||
idTablePut(symMap, s, x)
|
||||
n.sym = x
|
||||
else:
|
||||
@@ -220,7 +222,7 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool =
|
||||
if referencesAnotherParam(n[i], p): return true
|
||||
return false
|
||||
|
||||
proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
prc: PSym, info: TLineInfo) =
|
||||
# XXX: Instantiates a generic proc signature, while at the same
|
||||
# time adding the instantiated proc params into the current scope.
|
||||
@@ -237,7 +239,7 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
# will need to use openScope, addDecl, etc.
|
||||
#addDecl(c, prc)
|
||||
pushInfoContext(c.config, info)
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
|
||||
var cl = initTypeVars(c, typeMap, info, nil)
|
||||
var result = instCopyType(cl, prc.typ)
|
||||
let originalParams = result.n
|
||||
@@ -253,9 +255,15 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
var typeToFit = resulti
|
||||
|
||||
let needsStaticSkipping = resulti.kind == tyFromExpr
|
||||
let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
|
||||
if resulti.kind == tyFromExpr:
|
||||
resulti.flags.incl tfNonConstExpr
|
||||
result[i] = replaceTypeVarsT(cl, resulti)
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
if needsTypeDescSkipping:
|
||||
result[i] = result[i].skipTypes({tyTypeDesc})
|
||||
typeToFit = result[i]
|
||||
|
||||
# ...otherwise, we use the instantiated type in `fitNode`
|
||||
if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
|
||||
@@ -265,7 +273,7 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam, c.idgen)
|
||||
param.owner = prc
|
||||
setOwner(param, prc)
|
||||
param.typ = result[i]
|
||||
|
||||
# The default value is instantiated and fitted against the final
|
||||
@@ -273,9 +281,10 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
# call head symbol, because this leads to infinite recursion.
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind in nkCallKinds:
|
||||
for i in 1..<def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i], 1)
|
||||
if def.typ.kind == tyFromExpr:
|
||||
def.typ.flags.incl tfNonConstExpr
|
||||
if not isIntLit(def.typ):
|
||||
def = prepareNode(cl, def)
|
||||
|
||||
# allow symchoice since node will be fit later
|
||||
# although expectedType should cover it
|
||||
@@ -292,6 +301,8 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
# the user calls an explicit instantiation of the proc (this is
|
||||
# the only way the default value might be inserted).
|
||||
param.ast = errorNode(c, def)
|
||||
# we know the node is empty, we need the actual type for error message
|
||||
param.ast.typ() = def.typ
|
||||
else:
|
||||
param.ast = fitNodePostMatch(c, typeToFit, converted)
|
||||
param.typ = result[i]
|
||||
@@ -315,6 +326,22 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
|
||||
prc.typ = result
|
||||
popInfoContext(c.config)
|
||||
|
||||
proc instantiateOnlyProcType(c: PContext, pt: LayeredIdTable, prc: PSym, info: TLineInfo): PType =
|
||||
# instantiates only the type of a given proc symbol
|
||||
# used by sigmatch for explicit generics
|
||||
# wouldn't be needed if sigmatch could handle complex cases,
|
||||
# examples are in texplicitgenerics
|
||||
# might be buggy, see rest of generateInstance if problems occur
|
||||
let fakeSym = copySym(prc, c.idgen)
|
||||
incl(fakeSym.flags, sfFromGeneric)
|
||||
fakeSym.instantiatedFrom = prc
|
||||
openScope(c)
|
||||
for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
|
||||
addDecl(c, s)
|
||||
instantiateProcType(c, pt, fakeSym, info)
|
||||
closeScope(c)
|
||||
result = fakeSym.typ
|
||||
|
||||
proc fillMixinScope(c: PContext) =
|
||||
var p = c.p
|
||||
while p != nil:
|
||||
@@ -335,7 +362,7 @@ proc getLocalPassC(c: PContext, s: PSym): string =
|
||||
for p in n:
|
||||
extractPassc(p)
|
||||
|
||||
proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
|
||||
proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
info: TLineInfo): PSym =
|
||||
## Generates a new instance of a generic procedure.
|
||||
## The `pt` parameter is a type-unsafe mapping table used to link generic
|
||||
@@ -346,7 +373,11 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
|
||||
if c.instCounter > 50:
|
||||
globalError(c.config, info, "generic instantiation too nested")
|
||||
inc c.instCounter
|
||||
defer: dec c.instCounter
|
||||
let currentTypeofContext = c.inTypeofContext
|
||||
c.inTypeofContext = 0
|
||||
defer:
|
||||
dec c.instCounter
|
||||
c.inTypeofContext = currentTypeofContext
|
||||
# careful! we copy the whole AST including the possibly nil body!
|
||||
var n = copyTree(fn.ast)
|
||||
# NOTE: for access of private fields within generics from a different module
|
||||
@@ -364,9 +395,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
|
||||
let passc = getLocalPassC(c, producer)
|
||||
if passc != "": #pass the local compiler options to the consumer module too
|
||||
extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex))
|
||||
result.owner = c.module
|
||||
setOwner(result, c.module)
|
||||
else:
|
||||
result.owner = fn
|
||||
setOwner(result, fn)
|
||||
result.ast = n
|
||||
pushOwner(c, result)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -50,24 +93,27 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
case n.kind
|
||||
of nkObjConstr:
|
||||
let x = t.skipTypes(abstractPtrs)
|
||||
n.typ = t
|
||||
n[0].typ = t
|
||||
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
|
||||
n.typ() = t
|
||||
for i in 0..<n.len:
|
||||
if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
|
||||
else: annotateType(n[i], x[i], conf)
|
||||
elif x.kind == tyProc and x.callConv == ccClosure:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
|
||||
if n.kind == nkTupleConstr:
|
||||
let
|
||||
@@ -88,39 +134,39 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
globalError(conf, n.info, "Incorrectly generated tuple constr")
|
||||
n[] = bracketExpr[]
|
||||
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
else:
|
||||
globalError(conf, n.info, "() must have a tuple type")
|
||||
of nkBracket:
|
||||
if x.kind in {tyArray, tySequence, tyOpenArray}:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
for m in n: annotateType(m, x.elemType, conf)
|
||||
else:
|
||||
globalError(conf, n.info, "[] must have some form of array type")
|
||||
of nkCurly:
|
||||
if x.kind in {tySet}:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
for m in n: annotateType(m, x.elemType, conf)
|
||||
else:
|
||||
globalError(conf, n.info, "{} must have the set type")
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
if x.kind in {tyFloat..tyFloat128}:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
else:
|
||||
globalError(conf, n.info, "float literal must have some float type")
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
else:
|
||||
globalError(conf, n.info, "integer literal must have some int type")
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if x.kind in {tyString, tyCstring}:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
else:
|
||||
globalError(conf, n.info, "string literal must be of some string type")
|
||||
of nkNilLit:
|
||||
if x.kind in NilableTypes+{tyString, tySequence}:
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
else:
|
||||
globalError(conf, n.info, "nil literal must be of some pointer type")
|
||||
else: discard
|
||||
|
||||
@@ -18,7 +18,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
|
||||
let typ = result[1].typ # new(x)
|
||||
if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject:
|
||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
|
||||
asgnExpr.typ = typ
|
||||
asgnExpr.typ() = typ
|
||||
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
|
||||
while true:
|
||||
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
|
||||
@@ -38,7 +38,7 @@ proc semAddr(c: PContext; n: PNode): PNode =
|
||||
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
|
||||
localError(c.config, n.info, errExprHasNoAddress)
|
||||
result.add x
|
||||
result.typ = makePtrType(c, x.typ)
|
||||
result.typ() = makePtrType(c, x.typ)
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
@@ -49,26 +49,50 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
else:
|
||||
m = mode.intVal
|
||||
result = newNodeI(nkTypeOfExpr, n.info)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
result.add typExpr
|
||||
result.typ = makeTypeDesc(c, typExpr.typ)
|
||||
if typExpr.typ.kind == tyFromExpr:
|
||||
typExpr.typ.flags.incl tfNonConstExpr
|
||||
result.typ() = makeTypeDesc(c, typExpr.typ)
|
||||
|
||||
type
|
||||
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
|
||||
|
||||
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
|
||||
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = false): PNode
|
||||
|
||||
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
result = newNodeI(nkBracketExpr, n.info)
|
||||
for i in 1..<n.len: result.add(n[i])
|
||||
result = semSubscript(c, result, flags)
|
||||
result = semSubscript(c, result, flags, afterOverloading = true)
|
||||
if result.isNil:
|
||||
let x = copyTree(n)
|
||||
x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
|
||||
bracketNotFoundError(c, x)
|
||||
#localError(c.config, n.info, "could not resolve: " & $n)
|
||||
result = errorNode(c, n)
|
||||
if c.inGenericContext > 0:
|
||||
for i in 0..<n.len:
|
||||
let a = n[i]
|
||||
if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}:
|
||||
# expression is compiled early in a generic body
|
||||
result = semGenericStmt(c, x)
|
||||
result.typ() = makeTypeFromExpr(c, copyTree(result))
|
||||
result.typ.flags.incl tfNonConstExpr
|
||||
return
|
||||
let s = # extract sym from first arg
|
||||
if n.len > 1:
|
||||
if n[1].kind == nkSym: n[1].sym
|
||||
elif n[1].kind in nkSymChoices + {nkOpenSym} and n[1].len != 0:
|
||||
n[1][0].sym
|
||||
else: nil
|
||||
else: nil
|
||||
if s != nil and s.kind in routineKinds:
|
||||
# this is a failed generic instantiation
|
||||
# semSubscript should already error but this is better for cascading errors
|
||||
result = explicitGenericInstError(c, n)
|
||||
else:
|
||||
bracketNotFoundError(c, x, flags)
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
|
||||
@@ -176,15 +200,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
let preferStr = traitCall[2].strVal
|
||||
prefer = parseEnum[TPreferedDesc](preferStr)
|
||||
result = newStrNode(nkStrLit, operand.typeToString(prefer))
|
||||
result.typ = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.typ() = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.info = traitCall.info
|
||||
of "name", "$":
|
||||
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
|
||||
result.typ = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.typ() = getSysType(c.graph, traitCall[1].info, tyString)
|
||||
result.info = traitCall.info
|
||||
of "arity":
|
||||
result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
|
||||
result.typ = newType(tyInt, c.idgen, context)
|
||||
result.typ() = newType(tyInt, c.idgen, context)
|
||||
result.info = traitCall.info
|
||||
of "genericHead":
|
||||
var arg = operand
|
||||
@@ -224,8 +248,9 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
of "rangeBase":
|
||||
# return the base type of a range type
|
||||
var arg = operand.skipTypes({tyGenericInst})
|
||||
assert arg.kind == tyRange
|
||||
result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info)
|
||||
if arg.kind == tyRange:
|
||||
arg = arg.base
|
||||
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
|
||||
of "isCyclic":
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
let isCyclic = canFormAcycle(c.graph, operand)
|
||||
@@ -253,7 +278,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
|
||||
discard
|
||||
else:
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(parType, preferDesc))
|
||||
result.typ = errorType(c)
|
||||
result.typ() = errorType(c)
|
||||
|
||||
proc semBindSym(c: PContext, n: PNode): PNode =
|
||||
result = copyNode(n)
|
||||
@@ -369,7 +394,7 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||
message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n))
|
||||
result = newIntNode(nkIntLit, 1)
|
||||
result.info = n.info
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
result.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
return result
|
||||
elif diff == high(int):
|
||||
if commonSuperclass(a, b) == nil:
|
||||
@@ -378,10 +403,10 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||
message(c.config, n.info, hintConditionAlwaysFalse, renderTree(n))
|
||||
result = newIntNode(nkIntLit, 0)
|
||||
result.info = n.info
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
result.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
else:
|
||||
localError(c.config, n.info, "'of' takes 2 arguments")
|
||||
n.typ = getSysType(c.graph, n.info, tyBool)
|
||||
n.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
|
||||
proc semUnown(c: PContext; n: PNode): PNode =
|
||||
@@ -416,9 +441,9 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
result = t
|
||||
|
||||
result = copyTree(n[1])
|
||||
result.typ = unownedType(c, result.typ)
|
||||
result.typ() = unownedType(c, result.typ)
|
||||
# little hack for injectdestructors.nim (see bug #11350):
|
||||
#result[0].typ = nil
|
||||
#result[0].typ() = nil
|
||||
|
||||
proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym =
|
||||
# We need to do 2 things: Replace n.typ which is a 'ref T' by a 'var T' type.
|
||||
@@ -428,7 +453,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
proc transform(c: PContext; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
|
||||
result = shallowCopy(n)
|
||||
if sameTypeOrNil(n.typ, old):
|
||||
result.typ = fresh
|
||||
result.typ() = fresh
|
||||
if n.kind == nkSym and n.sym == oldParam:
|
||||
result.sym = newParam
|
||||
for i in 0 ..< safeLen(n):
|
||||
@@ -439,7 +464,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
result = copySym(orig, c.idgen)
|
||||
result.info = info
|
||||
result.flags.incl sfFromGeneric
|
||||
result.owner = orig
|
||||
setOwner(result, orig)
|
||||
let origParamType = orig.typ.firstParamType
|
||||
let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
|
||||
let oldParam = orig.typ.n[1].sym
|
||||
@@ -510,32 +535,34 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
discard "already turned this one into a finalizer"
|
||||
else:
|
||||
if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
|
||||
fin.owner = 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
|
||||
setOwner(fin, fin.instantiatedFrom)
|
||||
|
||||
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)
|
||||
transFormedSym.owner = 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 =
|
||||
@@ -587,7 +614,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mTypeTrait: result = semTypeTraits(c, n)
|
||||
of mAstToStr:
|
||||
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
|
||||
result.typ = getSysType(c.graph, n.info, tyString)
|
||||
result.typ() = getSysType(c.graph, n.info, tyString)
|
||||
of mInstantiationInfo: result = semInstantiationInfo(c, n)
|
||||
of mOrd: result = semOrd(c, n)
|
||||
of mOf: result = semOf(c, n)
|
||||
@@ -600,7 +627,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result = semDynamicBindSym(c, n)
|
||||
of mProcCall:
|
||||
result = n
|
||||
result.typ = n[1].typ
|
||||
result.typ() = n[1].typ
|
||||
of mDotDot:
|
||||
result = n
|
||||
of mPlugin:
|
||||
@@ -643,7 +670,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ.len == 3:
|
||||
let boolLit = newIntLit(c.graph, n.info, 1)
|
||||
boolLit.typ = getSysType(c.graph, n.info, tyBool)
|
||||
boolLit.typ() = getSysType(c.graph, n.info, tyBool)
|
||||
result.add boolLit
|
||||
of mWasMoved:
|
||||
result = n
|
||||
@@ -685,7 +712,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result = n
|
||||
if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and
|
||||
expectedType.kind == tySequence and result.typ.elementType.kind == tyEmpty:
|
||||
result.typ = expectedType # type inference for empty sequence # bug #21377
|
||||
result.typ() = expectedType # type inference for empty sequence # bug #21377
|
||||
of mEnsureMove:
|
||||
result = n
|
||||
if n[1].kind in {nkStmtListExpr, nkBlockExpr,
|
||||
|
||||
@@ -188,7 +188,7 @@ proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
|
||||
newNodeIT(nkType, constrCtx.initExpr.info, asgnType)
|
||||
)
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
asgnExpr.typ = recTyp
|
||||
asgnExpr.typ() = recTyp
|
||||
defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
|
||||
|
||||
proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
|
||||
@@ -387,10 +387,13 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
if e != nil:
|
||||
result.status = initFull
|
||||
elif field.ast != nil:
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||
if efIgnoreDefaults notin flags:
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||
else:
|
||||
result.status = initNone
|
||||
else:
|
||||
if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
|
||||
if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass
|
||||
let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
|
||||
if defaultExpr != nil:
|
||||
result.status = initUnknown
|
||||
@@ -443,7 +446,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
||||
assert objType != nil
|
||||
if objType.kind == tyObject:
|
||||
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
|
||||
if constrCtx.missingFields.len > 0:
|
||||
localError(c.config, info,
|
||||
"The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
|
||||
@@ -462,17 +465,20 @@ 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:
|
||||
t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
# we have to watch out, there are also 'owned proc' types that can be used
|
||||
# multiple times as long as they don't have closures.
|
||||
result.typ.flags.incl tfHasOwned
|
||||
|
||||
@@ -407,9 +407,9 @@ proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode =
|
||||
result = copyNode(n)
|
||||
var typ = newType(tyOpenArray, idgen, result.typ.owner)
|
||||
typ.add result.typ.elementType
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice))
|
||||
opSlice.typ = getSysType(g, n.info, tyInt)
|
||||
opSlice.typ() = getSysType(g, n.info, tyInt)
|
||||
result.add opSlice
|
||||
result.add n[1]
|
||||
let slice = n[2].skipStmtList
|
||||
|
||||
@@ -11,7 +11,7 @@ import
|
||||
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
wordrecg, options, guards, lineinfos, semfold, semdata,
|
||||
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
|
||||
semstrictfuncs, suggestsymdb
|
||||
semstrictfuncs, suggestsymdb, pushpoppragmas
|
||||
|
||||
import std/[tables, intsets, strutils, sequtils]
|
||||
|
||||
@@ -85,6 +85,7 @@ type
|
||||
isInnerProc: bool
|
||||
inEnforcedNoSideEffects: bool
|
||||
currOptions: TOptions
|
||||
optionsStack: seq[(TOptions, TNoteKinds)]
|
||||
config: ConfigRef
|
||||
graph: ModuleGraph
|
||||
c: PContext
|
||||
@@ -192,7 +193,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
|
||||
let dot = newNodeI(nkDotExpr, n.info, 2)
|
||||
dot[0] = n[0]
|
||||
dot[1] = newSymNode(g)
|
||||
dot.typ = g.typ
|
||||
dot.typ() = g.typ
|
||||
for L in a.locked:
|
||||
#if a.guards.sameSubexprs(dot, L): return
|
||||
if guards.sameTree(dot, L): return
|
||||
@@ -211,6 +212,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
|
||||
@@ -219,7 +221,7 @@ proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||
if volatileCheck: makeVolatile(a, s)
|
||||
for x in a.init:
|
||||
if x == s.id:
|
||||
if strictDefs in a.c.features and s.kind == skLet:
|
||||
if noStrictDefs notin a.c.config.legacyFeatures and s.kind == skLet:
|
||||
localError(a.config, n.info, errXCannotBeAssignedTo %
|
||||
renderTree(n, {renderNoComments}
|
||||
))
|
||||
@@ -377,7 +379,7 @@ proc useVar(a: PEffects, n: PNode) =
|
||||
if s.typ.requiresInit:
|
||||
message(a.config, n.info, warnProveInit, s.name.s)
|
||||
elif a.leftPartOfAsgn <= 0:
|
||||
if strictDefs in a.c.features:
|
||||
if noStrictDefs notin a.c.config.legacyFeatures:
|
||||
if s.kind == skLet:
|
||||
localError(a.config, n.info, errLetNeedsInit)
|
||||
else:
|
||||
@@ -410,7 +412,7 @@ proc throws(tracked, n, orig: PNode) =
|
||||
if n.typ == nil or n.typ.kind != tyError:
|
||||
if orig != nil:
|
||||
let x = copyTree(orig)
|
||||
x.typ = n.typ
|
||||
x.typ() = n.typ
|
||||
tracked.add x
|
||||
else:
|
||||
tracked.add n
|
||||
@@ -425,12 +427,12 @@ proc excType(g: ModuleGraph; n: PNode): PType =
|
||||
|
||||
proc createRaise(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newNode(nkType)
|
||||
result.typ = getEbase(g, n.info)
|
||||
result.typ() = getEbase(g, n.info)
|
||||
if not n.isNil: result.info = n.info
|
||||
|
||||
proc createTag(g: ModuleGraph; n: PNode): PNode =
|
||||
result = newNode(nkType)
|
||||
result.typ = g.sysTypeFromName(n.info, "RootEffect")
|
||||
result.typ() = g.sysTypeFromName(n.info, "RootEffect")
|
||||
if not n.isNil: result.info = n.info
|
||||
|
||||
proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
|
||||
@@ -615,9 +617,16 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
|
||||
for i in 0..<n.len:
|
||||
var it = n[i]
|
||||
let pragma = whichPragma(it)
|
||||
if pragma == wEffects:
|
||||
case pragma
|
||||
of wEffects:
|
||||
# list the computed effects up to here:
|
||||
listEffects(tracked)
|
||||
of wPush:
|
||||
processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1)
|
||||
of wPop:
|
||||
processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions)
|
||||
else:
|
||||
discard
|
||||
|
||||
template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
|
||||
|
||||
@@ -1202,7 +1211,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
|
||||
if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ() = n.sym.typ
|
||||
of nkHiddenAddr, nkAddr:
|
||||
if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and
|
||||
n.typ.kind notin {tyVar, tyLent}:
|
||||
@@ -1293,7 +1302,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?
|
||||
@@ -1594,7 +1608,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects
|
||||
result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects],
|
||||
forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule,
|
||||
init: @[], locked: @[], graph: g, config: g.config, c: c,
|
||||
currentBlock: 1
|
||||
currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)]
|
||||
)
|
||||
result.guards.s = @[]
|
||||
result.guards.g = g
|
||||
@@ -1647,7 +1661,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
|
||||
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
|
||||
noStrictDefs notin c.config.legacyFeatures) and
|
||||
s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
|
||||
var res = s.ast[resultPos].sym # get result symbol
|
||||
if res.id notin t.init and breaksBlock(body) != bsNoReturn:
|
||||
|
||||
@@ -112,11 +112,11 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
dec(c.p.nestedLoopCounter)
|
||||
closeScope(c)
|
||||
if n[1].typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
result.typ() = c.enforceVoidContext
|
||||
elif efInTypeof in flags:
|
||||
result.typ = n[1].typ
|
||||
result.typ() = n[1].typ
|
||||
elif implicitlyDiscardable(n[1]):
|
||||
result[1].typ = c.enforceVoidContext
|
||||
result[1].typ() = c.enforceVoidContext
|
||||
|
||||
proc semProc(c: PContext, n: PNode): PNode
|
||||
|
||||
@@ -275,7 +275,7 @@ proc fixNilType(c: PContext; n: PNode) =
|
||||
elif n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
n.transitionSonsKind(nkStmtList)
|
||||
for it in n: fixNilType(c, it)
|
||||
n.typ = nil
|
||||
n.typ() = nil
|
||||
|
||||
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
if c.matchedConcept != nil or efInTypeof in flags: return
|
||||
@@ -331,14 +331,14 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil):
|
||||
for it in n: discardCheck(c, it.lastSon, flags)
|
||||
result.transitionSonsKind(nkIfStmt)
|
||||
# propagate any enforced VoidContext:
|
||||
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
|
||||
if typ == c.enforceVoidContext: result.typ() = c.enforceVoidContext
|
||||
else:
|
||||
for it in n:
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it[j]):
|
||||
it[j] = fitNode(c, typ, it[j], it[j].info)
|
||||
result.transitionSonsKind(nkIfExpr)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
var check = initIntSet()
|
||||
@@ -438,7 +438,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
discardCheck(c, n[0], flags)
|
||||
for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
result.typ() = c.enforceVoidContext
|
||||
else:
|
||||
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
|
||||
if not endsInNoReturn(n[0]):
|
||||
@@ -448,7 +448,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it[j]):
|
||||
it[j] = fitNode(c, typ, it[j], it[j].info)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
result = fitNode(c, typ, n, n.info)
|
||||
@@ -457,7 +457,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}:
|
||||
result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal)
|
||||
result.info = n.info
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
if not floatRangeCheck(result.floatVal, typ):
|
||||
localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
|
||||
elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
|
||||
@@ -605,7 +605,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
obj.n.add newSymNode(field)
|
||||
n[0] = makeDeref x
|
||||
n[1] = newSymNode(field)
|
||||
n.typ = field.typ
|
||||
n.typ() = field.typ
|
||||
else:
|
||||
localError(c.config, n.info, "implicit object field construction " &
|
||||
"requires a .partial object, but got " & typeToString(obj))
|
||||
@@ -622,15 +622,15 @@ proc setVarType(c: PContext; v: PSym, typ: PType) =
|
||||
proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
|
||||
# make sure it's not a normal pragma, and calls an identifier
|
||||
# considerQuotedIdent below will fail on non-identifiers
|
||||
result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds
|
||||
result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr}
|
||||
if result:
|
||||
# make sure it's not a user pragma
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
result = strTableGet(c.userPragmas, ident) == nil
|
||||
if key.kind != nkDotExpr:
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
result = strTableGet(c.userPragmas, ident) == nil
|
||||
if result:
|
||||
# make sure it's not a custom pragma
|
||||
var amb = false
|
||||
let sym = searchInScopes(c, ident, amb)
|
||||
let sym = qualifiedLookUp(c, key, {})
|
||||
result = sym == nil or sfCustomPragma notin sym.flags
|
||||
|
||||
proc copyExcept(n: PNode, i: int): PNode =
|
||||
@@ -829,6 +829,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
var typFlags: TTypeAllowedFlags = {}
|
||||
|
||||
var def: PNode = c.graph.emptyNode
|
||||
if typ != nil and typ.kind == tyRange and
|
||||
c.graph.config.isDefined("nimPreviewRangeDefault") and
|
||||
a[^1].kind == nkEmpty:
|
||||
a[^1] = firstRange(c.config, typ)
|
||||
|
||||
if a[^1].kind != nkEmpty:
|
||||
def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
|
||||
|
||||
@@ -903,7 +908,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
if sfGenSym notin v.flags:
|
||||
if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
|
||||
else:
|
||||
if v.owner == nil: v.owner = c.p.owner
|
||||
if v.owner == nil: setOwner(v, c.p.owner)
|
||||
when oKeepVariableNames:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
else:
|
||||
@@ -943,7 +948,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
else:
|
||||
checkNilable(c, v)
|
||||
# allow let to not be initialised if imported from C:
|
||||
if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)):
|
||||
if v.kind == skLet and sfImportc notin v.flags and (noStrictDefs in c.config.legacyFeatures or not isLocalSym(v)):
|
||||
localError(c.config, a.info, errLetNeedsInit)
|
||||
if sfCompileTime in v.flags:
|
||||
var x = newNodeI(result.kind, v.info)
|
||||
@@ -980,6 +985,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
var typFlags: TTypeAllowedFlags = {}
|
||||
|
||||
# don't evaluate here since the type compatibility check below may add a converter
|
||||
openScope(c)
|
||||
var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
|
||||
|
||||
if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
|
||||
@@ -1006,6 +1012,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
if c.matchedConcept != nil:
|
||||
typFlags.incl taConcept
|
||||
typeAllowedCheck(c, a.info, typ, skConst, typFlags)
|
||||
closeScope(c)
|
||||
|
||||
if a.kind == nkVarTuple:
|
||||
# generate new section from tuple unpacking and embed it into this one
|
||||
@@ -1019,7 +1026,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
when defined(nimsuggest):
|
||||
v.hasUserSpecifiedType = hasUserSpecifiedType
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
|
||||
@@ -1090,7 +1097,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
v.typ = iter[i]
|
||||
n[0][i] = newSymNode(v)
|
||||
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
else:
|
||||
var v = symForVar(c, n[0])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
@@ -1100,7 +1107,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
v.typ = iterBase
|
||||
n[0] = newSymNode(v)
|
||||
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
else:
|
||||
localError(c.config, n.info, errWrongNumberOfVariables)
|
||||
elif n.len-2 != iterAfterVarLent.len:
|
||||
@@ -1134,7 +1141,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
v.typ = iter[i][j]
|
||||
n[i][j] = newSymNode(v)
|
||||
if not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
else:
|
||||
var v = symForVar(c, n[i])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
@@ -1149,7 +1156,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
n[i] = newSymNode(v)
|
||||
if sfGenSym notin v.flags:
|
||||
if not isDiscardUnderscore(v): addDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
inc(c.p.nestedLoopCounter)
|
||||
let oldBreakInLoop = c.p.breakInLoop
|
||||
c.p.breakInLoop = true
|
||||
@@ -1224,7 +1231,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
toResolve.add n[0]
|
||||
|
||||
var errors: CandidateErrors = @[]
|
||||
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoDiagnostics},
|
||||
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared},
|
||||
errors, false)
|
||||
if r.state == csMatch:
|
||||
var match = r.calleeSym
|
||||
@@ -1238,8 +1245,6 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags)
|
||||
of skTemplate: result = semTemplateExpr(c, toExpand, match, flags)
|
||||
else: result = errorNode(c, n[0])
|
||||
elif r.state == csNoMatch:
|
||||
result = errorNode(c, n[0])
|
||||
else:
|
||||
result = errorNode(c, n[0])
|
||||
if result.kind == nkEmpty:
|
||||
@@ -1286,9 +1291,9 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
result = semForVars(c, n, flags)
|
||||
# propagate any enforced VoidContext:
|
||||
if n[^1].typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
result.typ() = c.enforceVoidContext
|
||||
elif efInTypeof in flags:
|
||||
result.typ = result.lastSon.typ
|
||||
result.typ() = result.lastSon.typ
|
||||
closeScope(c)
|
||||
|
||||
proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
@@ -1368,14 +1373,14 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
|
||||
for i in 1..<n.len: discardCheck(c, n[i].lastSon, flags)
|
||||
# propagate any enforced VoidContext:
|
||||
if typ == c.enforceVoidContext:
|
||||
result.typ = c.enforceVoidContext
|
||||
result.typ() = c.enforceVoidContext
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
var it = n[i]
|
||||
let j = it.len-1
|
||||
if not endsInNoReturn(it[j]):
|
||||
it[j] = fitNode(c, typ, it[j], it[j].info)
|
||||
result.typ = typ
|
||||
result.typ() = typ
|
||||
|
||||
proc semRaise(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
@@ -1470,7 +1475,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||
s = typsym
|
||||
# add it here, so that recursive types are possible:
|
||||
if sfGenSym notin s.flags: addInterfaceDecl(c, s)
|
||||
elif s.owner == nil: s.owner = getCurrOwner(c)
|
||||
elif s.owner == nil: setOwner(s, getCurrOwner(c))
|
||||
|
||||
if name.kind == nkPragmaExpr:
|
||||
if name[0].kind == nkPostfix:
|
||||
@@ -1785,10 +1790,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
checkForMetaFields(c, baseType.n, hasError)
|
||||
if not hasError:
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
|
||||
# fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
|
||||
if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
|
||||
incl(s.flags, sfGenSym)
|
||||
#instAllTypeBoundOp(c, n.info)
|
||||
|
||||
|
||||
@@ -1968,13 +1969,13 @@ proc semProcAnnotation(c: PContext, prc: PNode;
|
||||
|
||||
return result
|
||||
|
||||
proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
|
||||
proc semInferredLambda(c: PContext, pt: LayeredIdTable, n: PNode): PNode =
|
||||
## used for resolving 'auto' in lambdas based on their callsite
|
||||
var n = n
|
||||
let original = n[namePos].sym
|
||||
let s = original #copySym(original, false)
|
||||
#incl(s.flags, sfFromGeneric)
|
||||
#s.owner = original
|
||||
#s.owner() = original
|
||||
|
||||
n = replaceTypesInBody(c, pt, n, original)
|
||||
result = n
|
||||
@@ -1989,7 +1990,7 @@ proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
|
||||
tyFromExpr}+tyTypeClasses:
|
||||
localError(c.config, params[i].info, "cannot infer type of parameter: " &
|
||||
params[i].sym.name.s)
|
||||
#params[i].sym.owner = s
|
||||
#params[i].sym.owner() = s
|
||||
openScope(c)
|
||||
pushOwner(c, s)
|
||||
addParams(c, params, skProc)
|
||||
@@ -2001,7 +2002,7 @@ proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
|
||||
popOwner(c)
|
||||
closeScope(c)
|
||||
if optOwnedRefs in c.config.globalOptions and result.typ != nil:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
# alternative variant (not quite working):
|
||||
# var prc = arg[0].sym
|
||||
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
|
||||
@@ -2034,14 +2035,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:
|
||||
@@ -2075,10 +2089,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,
|
||||
@@ -2091,7 +2105,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
|
||||
@@ -2115,17 +2129,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,
|
||||
@@ -2216,10 +2230,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() & ")")
|
||||
@@ -2358,10 +2372,11 @@ 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
|
||||
s.owner = c.getCurrOwner
|
||||
setOwner(s, c.getCurrOwner)
|
||||
else:
|
||||
# Highlighting needs to be done early so the position for
|
||||
# name isn't changed (see taccent_highlight). We don't want to check if this is the
|
||||
@@ -2607,9 +2622,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
c.patterns.add(s)
|
||||
if isAnon:
|
||||
n.transitionSonsKind(nkLambda)
|
||||
result.typ = s.typ
|
||||
result.typ() = s.typ
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure:
|
||||
localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
|
||||
|
||||
@@ -2628,7 +2643,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
# gensym'ed iterator?
|
||||
if n[namePos].kind == nkSym:
|
||||
# gensym'ed iterators might need to become closure iterators:
|
||||
n[namePos].sym.owner = getCurrOwner(c)
|
||||
setOwner(n[namePos].sym, getCurrOwner(c))
|
||||
n[namePos].sym.transitionRoutineSymKind(skIterator)
|
||||
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
||||
# bug #7093: if after a macro transformation we don't have an
|
||||
@@ -2646,10 +2661,10 @@ 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)
|
||||
result.typ() = makeVarType(c, result.typ, tyOwned)
|
||||
result.typ.callConv = ccClosure
|
||||
|
||||
proc semProc(c: PContext, n: PNode): PNode =
|
||||
@@ -2734,20 +2749,23 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
|
||||
proc evalInclude(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
result.add n
|
||||
template checkAs(it: PNode) =
|
||||
if it.kind == nkInfix and it.len == 3:
|
||||
let op = it[0].getPIdent
|
||||
if op != nil and op.id == ord(wAs):
|
||||
localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.")
|
||||
for i in 0..<n.len:
|
||||
var imp: PNode
|
||||
let it = n[i]
|
||||
if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/":
|
||||
localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.")
|
||||
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
|
||||
let sep = it[0]
|
||||
let dir = it[1]
|
||||
imp = newNodeI(nkInfix, it.info)
|
||||
imp.add sep
|
||||
imp.add dir
|
||||
imp.add sep # dummy entry, replaced in the loop
|
||||
for x in it[2]:
|
||||
imp[2] = x
|
||||
checkAs(it)
|
||||
if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
|
||||
let lastPos = it.len - 1
|
||||
var imp = copyNode(it)
|
||||
newSons(imp, it.len)
|
||||
for i in 0 ..< lastPos: imp[i] = it[i]
|
||||
imp[lastPos] = imp[0] # dummy entry, replaced in the loop
|
||||
for x in it[lastPos]:
|
||||
checkAs(x)
|
||||
imp[lastPos] = x
|
||||
incMod(c, n, imp, result)
|
||||
else:
|
||||
incMod(c, n, it, result)
|
||||
@@ -2777,7 +2795,7 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
n[1] = semExpr(c, n[1], expectedType = expectedType)
|
||||
dec c.inUncheckedAssignSection, inUncheckedAssignSection
|
||||
result = n
|
||||
result.typ = n[1].typ
|
||||
result.typ() = n[1].typ
|
||||
for i in 0..<pragmaList.len:
|
||||
case whichPragma(pragmaList[i])
|
||||
of wLine: setInfoRecursive(result, pragmaList[i].info)
|
||||
@@ -2858,18 +2876,18 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
|
||||
let verdict = semConstExpr(c, n[i])
|
||||
if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0:
|
||||
localError(c.config, result.info, "concept predicate failed")
|
||||
of tyUnknown: continue
|
||||
of tyFromExpr: continue
|
||||
else: discard
|
||||
if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]):
|
||||
voidContext = true
|
||||
n.typ = c.enforceVoidContext
|
||||
n.typ() = c.enforceVoidContext
|
||||
if i == last and (n.len == 1 or ({efWantValue, efInTypeof} * flags != {})):
|
||||
n.typ = n[i].typ
|
||||
n.typ() = n[i].typ
|
||||
if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
|
||||
elif i != last or voidContext:
|
||||
discardCheck(c, n[i], flags)
|
||||
else:
|
||||
n.typ = n[i].typ
|
||||
n.typ() = n[i].typ
|
||||
if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
|
||||
var m = n[i]
|
||||
while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
|
||||
|
||||
@@ -218,43 +218,76 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
if k == skParam and c.inTemplateHeader > 0:
|
||||
local.flags.incl sfTemplateParam
|
||||
|
||||
proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
|
||||
proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
|
||||
incl(s.flags, sfUsed)
|
||||
# bug #12885; ideally sem'checking is performed again afterwards marking
|
||||
# the symbol as used properly, but the nfSem mechanism currently prevents
|
||||
# that from happening, so we mark the module as used here already:
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
markOwnerModuleAsUsed(c.c, s)
|
||||
# we do not call onUse here, as the identifier is not really
|
||||
# resolved here. We will fixup the used identifiers later.
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
result = n
|
||||
of OverloadableSyms-{skTemplate,skMacro}:
|
||||
result = symChoice(c, n, s, scOpen, isField)
|
||||
of skTemplate, skMacro:
|
||||
result = symChoice(c, n, s, scOpen, isField)
|
||||
if result.kind == nkSym:
|
||||
# template/macro symbols might need to be semchecked again
|
||||
# prepareOperand etc don't do this without setting the type to nil
|
||||
result.typ = nil
|
||||
of OverloadableSyms:
|
||||
result = symChoice(c.c, n, s, scOpen, isField)
|
||||
if not isField and result.kind in {nkSym, nkOpenSymChoice}:
|
||||
if openSym in c.c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ() = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
of skGenericParam:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
if not isField and s.owner != c.owner:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
of skParam:
|
||||
result = n
|
||||
of skType:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
else:
|
||||
if isAmbiguous:
|
||||
# ambiguous types should be symchoices since lookup behaves
|
||||
# differently for them in regular expressions
|
||||
result = symChoice(c.c, n, s, scOpen, isField)
|
||||
else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
|
||||
if not isField and not (s.owner == c.owner and
|
||||
s.typ != nil and s.typ.kind == tyGenericParam) and
|
||||
result.kind in {nkSym, nkOpenSymChoice}:
|
||||
if openSym in c.c.features:
|
||||
if result.kind == nkSym:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.typ() = nil
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
else:
|
||||
if isField and sfGenSym in s.flags: result = n
|
||||
else: result = newSymNode(s, n.info)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if not isField:
|
||||
if openSym in c.c.features:
|
||||
result = newOpenSym(result)
|
||||
else:
|
||||
result.flags.incl nfDisabledOpenSym
|
||||
result.typ() = nil
|
||||
# Issue #12832
|
||||
when defined(nimsuggest):
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
|
||||
# field access (dot expr) will be handled by builtinFieldAccess
|
||||
if not isField:
|
||||
styleCheckUse(c, n.info, s)
|
||||
styleCheckUse(c.c, n.info, s)
|
||||
|
||||
proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
|
||||
result = n
|
||||
@@ -345,6 +378,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
if n.ident.id in c.toInject: return n
|
||||
c.c.isAmbiguous = false
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
|
||||
@@ -362,9 +396,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.kind in {skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
|
||||
result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
|
||||
of nkBind:
|
||||
result = semTemplBody(c, n[0])
|
||||
of nkBindStmt:
|
||||
@@ -501,13 +535,20 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
of nkConverterDef:
|
||||
result = semRoutineInTemplBody(c, n, skConverter)
|
||||
of nkPragmaExpr:
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
result = semTemplBodySons(c, n)
|
||||
of nkPostfix:
|
||||
result[1] = semTemplBody(c, n[1])
|
||||
of nkPragma:
|
||||
for x in n:
|
||||
if x.kind == nkExprColonExpr:
|
||||
x[1] = semTemplBody(c, x[1])
|
||||
for i in 0 ..< n.len:
|
||||
let x = n[i]
|
||||
let prag = whichPragma(x)
|
||||
if prag == wInvalid:
|
||||
# only sem if not a language-level pragma
|
||||
result[i] = semTemplBody(c, x)
|
||||
elif x.kind in nkPragmaCallKinds:
|
||||
# is pragma, but value still needs to be checked
|
||||
for j in 1 ..< x.len:
|
||||
x[j] = semTemplBody(c, x[j])
|
||||
of nkBracketExpr:
|
||||
if n.typ == nil:
|
||||
# if a[b] is nested inside a typed expression, don't convert it
|
||||
@@ -558,6 +599,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
of nkDotExpr, nkAccQuoted:
|
||||
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
|
||||
# so we use the generic code for nkDotExpr too
|
||||
c.c.isAmbiguous = false
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
# mirror the nkIdent case
|
||||
@@ -572,9 +614,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
elif contains(c.toMixin, s.name.id):
|
||||
return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
|
||||
else:
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.kind in {skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
return semTemplSymbol(c.c, n, s, c.noGenSym > 0)
|
||||
return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
|
||||
if n.kind == nkDotExpr:
|
||||
result = n
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
@@ -658,6 +700,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
pushOwner(c, s)
|
||||
openScope(c)
|
||||
n[namePos] = newSymNode(s)
|
||||
s.ast = n # for implicitPragmas to use
|
||||
pragmaCallable(c, s, n, templatePragmas)
|
||||
implicitPragmas(c, s, n.info, templatePragmas)
|
||||
|
||||
@@ -708,6 +751,17 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
c: c,
|
||||
owner: s
|
||||
)
|
||||
# handle default params:
|
||||
for i in 1..<s.typ.n.len:
|
||||
let param = s.typ.n[i].sym
|
||||
if param.ast != nil:
|
||||
# param default values need to be treated like template body:
|
||||
if sfDirty in s.flags:
|
||||
param.ast = semTemplBodyDirty(ctx, param.ast)
|
||||
else:
|
||||
param.ast = semTemplBody(ctx, param.ast)
|
||||
if param.ast.referencesAnotherParam(s):
|
||||
param.ast.flags.incl nfDefaultRefsParam
|
||||
if sfDirty in s.flags:
|
||||
n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos])
|
||||
else:
|
||||
@@ -717,11 +771,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
closeScope(c)
|
||||
popOwner(c)
|
||||
|
||||
# set the symbol AST after pragmas, at least. This stops pragma that have
|
||||
# been pushed (implicit) to be explicitly added to the template definition
|
||||
# and misapplied to the body. see #18113
|
||||
s.ast = n
|
||||
|
||||
if sfCustomPragma in s.flags:
|
||||
if n[bodyPos].kind != nkEmpty:
|
||||
localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
|
||||
|
||||
@@ -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 =
|
||||
@@ -475,6 +497,13 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
let t = semTypeNode(c, it, nil)
|
||||
addSonSkipIntLitChecked(c, result, t, it, c.idgen)
|
||||
|
||||
proc firstRange(config: ConfigRef, t: PType): PNode =
|
||||
if t.skipModifier().kind in tyFloat..tyFloat64:
|
||||
result = newFloatNode(nkFloatLit, firstFloat(t))
|
||||
else:
|
||||
result = newIntNode(nkIntLit, firstOrd(config, t))
|
||||
result.typ() = t
|
||||
|
||||
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
var typ: PType
|
||||
result = newOrPrevType(tyTuple, prev, c)
|
||||
@@ -487,12 +516,18 @@ 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:
|
||||
a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
|
||||
a[^1].typ = typ
|
||||
a[^1] = firstRange(c.config, typ)
|
||||
hasDefaultField = true
|
||||
else:
|
||||
localError(c.config, a.info, errTypeExpected)
|
||||
@@ -580,9 +615,14 @@ proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
|
||||
let bc = semConstExpr(c, b)
|
||||
if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
|
||||
localError(c.config, b.info, "range of string is invalid")
|
||||
let at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
|
||||
let bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
|
||||
|
||||
var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
|
||||
var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
|
||||
# the calls to fitNode may introduce calls to converters
|
||||
# mirrored with semCaseBranch for single elements
|
||||
if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
|
||||
at = semConstExpr(c, at)
|
||||
if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
|
||||
bt = semConstExpr(c, bt)
|
||||
result = newNodeI(nkRange, a.info)
|
||||
result.add(at)
|
||||
result.add(bt)
|
||||
@@ -613,6 +653,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
|
||||
var b = branch[i]
|
||||
if b.kind == nkRange:
|
||||
branch[i] = b
|
||||
# same check as in semBranchRange for exhaustiveness
|
||||
covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
|
||||
elif isRange(b):
|
||||
branch[i] = semCaseBranchRange(c, n, b, covered)
|
||||
else:
|
||||
@@ -628,8 +670,8 @@ proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
var tmp = fitNode(c, n[0].typ, r, r.info)
|
||||
# the call to fitNode may introduce a call to a converter
|
||||
if tmp.kind == nkHiddenCallConv or
|
||||
(tmp.kind == nkHiddenStdConv and n[0].typ.kind == tyCstring):
|
||||
# mirrored with semBranchRange
|
||||
if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
|
||||
tmp = semConstExpr(c, tmp)
|
||||
branch[i] = skipConv(tmp)
|
||||
inc(covered)
|
||||
@@ -783,6 +825,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
of nkRecWhen:
|
||||
var a = copyTree(n)
|
||||
var branch: PNode = nil # the branch to take
|
||||
var cannotResolve = false # no branch should be taken
|
||||
for i in 0..<a.len:
|
||||
var it = a[i]
|
||||
if it == nil: illFormedAst(n, c.config)
|
||||
@@ -800,24 +843,30 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
let e = semExprWithType(c, it[0], {efDetermineType})
|
||||
if e.typ.kind == tyFromExpr:
|
||||
it[0] = makeStaticExpr(c, e)
|
||||
cannotResolve = true
|
||||
else:
|
||||
it[0] = forceBool(c, e)
|
||||
let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
|
||||
if val == nil or val.kind != nkIntLit:
|
||||
cannotResolve = true
|
||||
elif not cannotResolve and val.intVal != 0 and branch == nil:
|
||||
branch = it[1]
|
||||
of nkElse:
|
||||
checkSonsLen(it, 1, c.config)
|
||||
if branch == nil: branch = it[0]
|
||||
if branch == nil and not cannotResolve: branch = it[0]
|
||||
idx = 0
|
||||
else: illFormedAst(n, c.config)
|
||||
if c.inGenericContext > 0:
|
||||
if c.inGenericContext > 0 and cannotResolve:
|
||||
# use a new check intset here for each branch:
|
||||
var newCheck: IntSet = check
|
||||
var newPos = pos
|
||||
var newf = newNodeI(nkRecList, n.info)
|
||||
semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
|
||||
it[idx] = if newf.len == 1: newf[0] else: newf
|
||||
if c.inGenericContext > 0:
|
||||
father.add a
|
||||
elif branch != nil:
|
||||
if branch != nil:
|
||||
semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
|
||||
elif cannotResolve:
|
||||
father.add a
|
||||
elif father.kind in {nkElse, nkOfBranch}:
|
||||
father.add newNodeI(nkRecList, n.info)
|
||||
of nkRecCase:
|
||||
@@ -838,16 +887,22 @@ 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)
|
||||
else:
|
||||
typ = semTypeNode(c, n[^2], nil)
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||
n[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
|
||||
n[^1].typ = typ
|
||||
n[^1] = firstRange(c.config, typ)
|
||||
hasDefaultField = true
|
||||
propagateToOwner(rectype, typ)
|
||||
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
|
||||
@@ -1077,7 +1132,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||
if sfGenSym in param.flags:
|
||||
# bug #XXX, fix the gensym'ed parameters owner:
|
||||
if param.owner == nil:
|
||||
param.owner = getCurrOwner(c)
|
||||
setOwner(param, getCurrOwner(c))
|
||||
else: addDecl(c, param)
|
||||
|
||||
template shouldHaveMeta(t) =
|
||||
@@ -1277,7 +1332,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
|
||||
result = semTypeNode(c, n[0], nil)
|
||||
constraint = semNodeKindConstraints(n, c.config, 1)
|
||||
elif n.kind == nkCall and
|
||||
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
|
||||
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
|
||||
considerQuotedIdent(c, n[0]).s == "{}":
|
||||
result = semTypeNode(c, n[1], nil)
|
||||
constraint = semNodeKindConstraints(n, c.config, 2)
|
||||
@@ -1307,6 +1362,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
result = newProcType(c, n.info, prev)
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
template isCurrentlyGeneric: bool =
|
||||
# genericParams might update as implicit generic params are added
|
||||
genericParams != nil and genericParams.len > 0
|
||||
|
||||
for i in 1..<n.len:
|
||||
var a = n[i]
|
||||
@@ -1327,7 +1385,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
hasDefault = a[^1].kind != nkEmpty
|
||||
|
||||
if hasType:
|
||||
let isGeneric = isCurrentlyGeneric()
|
||||
inc c.inGenericContext, ord(isGeneric)
|
||||
typ = semParamType(c, a[^2], constraint)
|
||||
dec c.inGenericContext, ord(isGeneric)
|
||||
# TODO: Disallow typed/untyped in procs in the compiler/stdlib
|
||||
if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
|
||||
if not isMagic(getCurrOwner(c)):
|
||||
@@ -1347,15 +1408,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
"either use ';' (semicolon) or explicitly write each default value")
|
||||
message(c.config, a.info, warnImplicitDefaultValue, msg)
|
||||
block determineType:
|
||||
var defTyp = typ
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
defTyp = nil
|
||||
def = semGenericStmt(c, def)
|
||||
if hasUnresolvedArgs(c, def):
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
var canBeVoid = false
|
||||
if kind == skTemplate:
|
||||
if typ != nil and typ.kind == tyUntyped:
|
||||
# don't do any typechecking or assign a type for
|
||||
# `untyped` parameter default value
|
||||
break determineType
|
||||
|
||||
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp)
|
||||
elif hasUnresolvedArgs(c, def):
|
||||
# template default value depends on other parameter
|
||||
# don't do any typechecking
|
||||
def.typ() = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
elif typ != nil and typ.kind == tyTyped:
|
||||
canBeVoid = true
|
||||
let isGeneric = isCurrentlyGeneric()
|
||||
inc c.inGenericContext, ord(isGeneric)
|
||||
if canBeVoid:
|
||||
def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
|
||||
else:
|
||||
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
|
||||
dec c.inGenericContext, ord(isGeneric)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
def.flags.incl nfDefaultRefsParam
|
||||
|
||||
@@ -1374,7 +1446,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
|
||||
typ.flags.incl tfCheckedForDestructor
|
||||
|
||||
else:
|
||||
elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
# example code that triggers it:
|
||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||
@@ -1427,11 +1499,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
onDef(a[j].info, arg)
|
||||
a[j] = newSymNode(arg)
|
||||
|
||||
var r: PType =
|
||||
if n[0].kind != nkEmpty:
|
||||
semTypeNode(c, n[0], nil)
|
||||
else:
|
||||
nil
|
||||
var r: PType = nil
|
||||
if n[0].kind != nkEmpty:
|
||||
let isGeneric = isCurrentlyGeneric()
|
||||
inc c.inGenericContext, ord(isGeneric)
|
||||
r = semTypeNode(c, n[0], nil)
|
||||
dec c.inGenericContext, ord(isGeneric)
|
||||
|
||||
if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
|
||||
# XXX: To implement the proposed change in the warning, just
|
||||
@@ -1482,9 +1555,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# XXX This rather hacky way keeps 'tflatmap' compiling:
|
||||
if tfHasMeta notin oldFlags:
|
||||
result.flags.excl tfHasMeta
|
||||
result.n.typ = r
|
||||
result.n.typ() = r
|
||||
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
if isCurrentlyGeneric():
|
||||
for n in genericParams:
|
||||
if {sfUsed, sfAnon} * n.sym.flags == {}:
|
||||
result.flags.incl tfUnresolved
|
||||
@@ -1499,8 +1572,8 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
||||
n[i] = semStmt(c, n[i], {})
|
||||
if n.len > 0:
|
||||
result = semTypeNode(c, n[^1], prev)
|
||||
n.typ = result
|
||||
n[^1].typ = result
|
||||
n.typ() = result
|
||||
n[^1].typ() = result
|
||||
else:
|
||||
result = nil
|
||||
|
||||
@@ -1513,15 +1586,15 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n[0].kind notin {nkEmpty, nkSym}:
|
||||
addDecl(c, newSymS(skLabel, n[0], c))
|
||||
result = semStmtListType(c, n[1], prev)
|
||||
n[1].typ = result
|
||||
n.typ = result
|
||||
n[1].typ() = result
|
||||
n.typ() = result
|
||||
closeScope(c)
|
||||
c.p.breakInLoop = oldBreakInLoop
|
||||
dec(c.p.nestedBlockCounter)
|
||||
|
||||
proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
|
||||
result = semTypeNode(c, n, nil)
|
||||
n.typ = makeTypeDesc(c, result)
|
||||
n.typ() = makeTypeDesc(c, result)
|
||||
|
||||
proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
|
||||
var
|
||||
@@ -1641,7 +1714,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
|
||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil:
|
||||
if prev != nil and (prev.kind == tyGenericBody or
|
||||
typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
|
||||
result = newTypeS(tyAlias, c)
|
||||
result.rawAddSon typeExpr
|
||||
result.sym = prev.sym
|
||||
@@ -1679,6 +1753,10 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
|
||||
# unnecessary new type creation
|
||||
let alias = maybeAliasType(c, result, prev)
|
||||
if alias != nil: result = alias
|
||||
elif n.typ.kind == tyFromExpr and c.inGenericContext > 0:
|
||||
# sometimes not possible to distinguish type from value in generic body,
|
||||
# for example `T.Foo`, so both are handled under `tyFromExpr`
|
||||
result = n.typ
|
||||
else:
|
||||
localError(c.config, n.info, "expected type, but got: " & n.renderTree)
|
||||
result = errorType(c)
|
||||
@@ -1764,12 +1842,15 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
|
||||
discard "builtin pragma"
|
||||
else:
|
||||
trySuggestPragmas(c, key)
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
if strTableGet(c.userPragmas, ident) != nil:
|
||||
let ident =
|
||||
if key.kind in nkIdentKinds:
|
||||
considerQuotedIdent(c, key)
|
||||
else:
|
||||
nil
|
||||
if ident != nil and strTableGet(c.userPragmas, ident) != nil:
|
||||
discard "User-defined pragma"
|
||||
else:
|
||||
var amb = false
|
||||
let sym = searchInScopes(c, ident, amb)
|
||||
let sym = qualifiedLookUp(c, key, {})
|
||||
# XXX: What to do here if amb is true?
|
||||
if sym != nil and sfCustomPragma in sym.flags:
|
||||
discard "Custom user pragma"
|
||||
@@ -1836,10 +1917,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n, {efInTypeof})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
if result.kind == tyFromExpr:
|
||||
result.flags.incl tfNonConstExpr
|
||||
|
||||
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
@@ -1850,10 +1935,14 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
|
||||
else:
|
||||
m = mode.intVal
|
||||
inc c.inTypeofContext
|
||||
defer: dec c.inTypeofContext # compiles can raise an exception
|
||||
let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
if result.kind == tyFromExpr:
|
||||
result.flags.incl tfNonConstExpr
|
||||
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkSym:
|
||||
@@ -1917,7 +2006,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
n.transitionNoneToSym()
|
||||
n.sym = result
|
||||
n.info = oldInfo
|
||||
n.typ = result.typ
|
||||
n.typ() = result.typ
|
||||
else:
|
||||
localError(c.config, n.info, "identifier expected")
|
||||
result = errorSym(c, n)
|
||||
@@ -1941,11 +2030,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkTupleConstr: result = semAnonTuple(c, n, prev)
|
||||
of nkCallKinds:
|
||||
let x = n[0]
|
||||
let ident = case x.kind
|
||||
of nkIdent: x.ident
|
||||
of nkSym: x.sym.name
|
||||
of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
|
||||
else: nil
|
||||
let ident = x.getPIdent
|
||||
if ident != nil and ident.s == "[]":
|
||||
let b = newNodeI(nkBracketExpr, n.info)
|
||||
for i in 1..<n.len: b.add(n[i])
|
||||
@@ -2035,20 +2120,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
elif op.id == ord(wType):
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = semTypeOf(c, n[1], prev)
|
||||
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
|
||||
elif op.s == "typeof" and (
|
||||
(n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
|
||||
(n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
|
||||
result = semTypeOf2(c, n, prev)
|
||||
elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
else:
|
||||
if c.inGenericContext > 0 and n.kind == nkCall:
|
||||
let n = semGenericStmt(c, n)
|
||||
result = makeTypeFromExpr(c, n.copyTree)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
result = semTypeExpr(c, n, prev)
|
||||
of nkWhenStmt:
|
||||
var whenResult = semWhen(c, n, false)
|
||||
if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
|
||||
result = semTypeNode(c, whenResult, prev)
|
||||
if whenResult.kind == nkWhenStmt:
|
||||
result = whenResult.typ
|
||||
else:
|
||||
result = semTypeNode(c, whenResult, prev)
|
||||
of nkBracketExpr:
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
var head = n[0]
|
||||
@@ -2093,6 +2179,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mRef: result = semAnyRef(c, n, tyRef, prev)
|
||||
of mPtr: result = semAnyRef(c, n, tyPtr, prev)
|
||||
of mTuple: result = semTuple(c, n, prev)
|
||||
of mBuiltinType:
|
||||
case s.name.s
|
||||
of "lent": result = semAnyRef(c, n, tyLent, prev)
|
||||
of "sink": result = semAnyRef(c, n, tySink, prev)
|
||||
of "owned": result = semAnyRef(c, n, tyOwned, prev)
|
||||
else: result = semGeneric(c, n, s, prev)
|
||||
else: result = semGeneric(c, n, s, prev)
|
||||
of nkDotExpr:
|
||||
let typeExpr = semExpr(c, n)
|
||||
@@ -2122,7 +2214,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
result = s.typ.base
|
||||
elif prev == nil:
|
||||
result = s.typ
|
||||
@@ -2146,7 +2238,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if s.kind == skType:
|
||||
s.typ
|
||||
else:
|
||||
internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
|
||||
internalAssert c.config, s.typ.base.kind != tyNone
|
||||
s.typ.base
|
||||
let alias = maybeAliasType(c, t, prev)
|
||||
if alias != nil:
|
||||
@@ -2207,12 +2299,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkType: result = n.typ
|
||||
of nkStmtListType: result = semStmtListType(c, n, prev)
|
||||
of nkBlockType: result = semBlockType(c, n, prev)
|
||||
of nkOpenSym: result = semTypeNode(c, n[0], prev)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
when false:
|
||||
localError(c.config, n.info, "type expected, but got: " & renderTree(n))
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
n.typ = result
|
||||
n.typ() = result
|
||||
dec c.inTypeContext
|
||||
|
||||
proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
||||
@@ -2359,7 +2452,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
else:
|
||||
# the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
|
||||
# from manyloc/named_argument_bug/triengine:
|
||||
def.typ = def.typ.skipTypes({tyTypeDesc})
|
||||
def.typ() = def.typ.skipTypes({tyTypeDesc})
|
||||
if not containsGenericType(def.typ):
|
||||
def = fitNode(c, typ, def, def.info)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import std / tables
|
||||
|
||||
import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
|
||||
lineinfos, modulegraphs
|
||||
lineinfos, modulegraphs, layeredtable
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -65,10 +65,6 @@ proc cacheTypeInst(c: PContext; inst: PType) =
|
||||
addToGenericCache(c, gt.sym, inst)
|
||||
|
||||
type
|
||||
LayeredIdTable* {.acyclic.} = ref object
|
||||
topLayer*: TypeMapping
|
||||
nextLayer*: LayeredIdTable
|
||||
|
||||
TReplTypeVars* = object
|
||||
c*: PContext
|
||||
typeMap*: LayeredIdTable # map PType to PType
|
||||
@@ -84,27 +80,12 @@ 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
|
||||
|
||||
proc initLayeredTypeMap*(pt: sink TypeMapping): LayeredIdTable =
|
||||
result = LayeredIdTable()
|
||||
result.topLayer = pt
|
||||
|
||||
proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable =
|
||||
result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initTable[ItemId, PType]())
|
||||
|
||||
proc lookup(typeMap: LayeredIdTable, key: PType): PType =
|
||||
result = nil
|
||||
var tm = typeMap
|
||||
while tm != nil:
|
||||
result = getOrDefault(tm.topLayer, key.itemId)
|
||||
if result != nil: return
|
||||
tm = tm.nextLayer
|
||||
|
||||
template put(typeMap: LayeredIdTable, key, value: PType) =
|
||||
typeMap.topLayer[key.itemId] = value
|
||||
result = newTypeMapLayer(cl.typeMap)
|
||||
|
||||
template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
|
||||
when false:
|
||||
@@ -114,32 +95,90 @@ 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 =
|
||||
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
## instantiates a given generic expression, not a type node
|
||||
if n.kind == nkSym and n.sym.kind == skType and
|
||||
n.sym.typ != nil and n.sym.typ.kind == tyGenericBody:
|
||||
# generic body types are allowed as user expressions, see #24090
|
||||
return n
|
||||
let t = replaceTypeVarsT(cl, n.typ)
|
||||
if t != nil and t.kind == tyStatic and t.n != nil:
|
||||
return if tfUnresolved in t.flags: prepareNode(cl, t.n)
|
||||
else: t.n
|
||||
result = copyNode(n)
|
||||
result.typ = t
|
||||
result.typ() = t
|
||||
if result.kind == nkSym:
|
||||
result.sym =
|
||||
if n.typ != nil and n.typ == n.sym.typ:
|
||||
replaceTypeVarsS(cl, n.sym, result.typ)
|
||||
else:
|
||||
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
|
||||
let isCall = result.kind in nkCallKinds
|
||||
# don't try to instantiate symchoice symbols, they can be
|
||||
# generic procs which the compiler will think are uninstantiated
|
||||
# because their type will contain uninstantiated params
|
||||
let isSymChoice = result.kind in nkSymChoices
|
||||
for i in 0..<n.safeLen:
|
||||
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
|
||||
if isSymChoice or (isCall and i == 0): result.add(n[i])
|
||||
else: result.add(prepareNode(cl, n[i]))
|
||||
# we need to avoid trying to instantiate nodes that can have uninstantiated
|
||||
# types, like generic proc symbols or raw generic type symbols
|
||||
case n.kind
|
||||
of nkSymChoices:
|
||||
# don't try to instantiate symchoice symbols, they can be
|
||||
# generic procs which the compiler will think are uninstantiated
|
||||
# because their type will contain uninstantiated params
|
||||
for i in 0..<n.len:
|
||||
result.add(n[i])
|
||||
of nkCallKinds:
|
||||
# don't try to instantiate call names since they may be generic proc syms
|
||||
# also bracket expressions can turn into calls with symchoice [] and
|
||||
# we need to not instantiate the Generic in Generic[int]
|
||||
# exception exists for the call name being a dot expression since
|
||||
# dot expressions need their LHS instantiated
|
||||
assert n.len != 0
|
||||
# avoid instantiating generic proc symbols, refine condition if needed:
|
||||
let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds
|
||||
let name = n[0].getPIdent
|
||||
let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and
|
||||
# generic type instantiation:
|
||||
((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or
|
||||
# generic proc instantiation:
|
||||
(n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict))
|
||||
if ignoreFirst:
|
||||
result.add(n[0])
|
||||
else:
|
||||
result.add(prepareNode(cl, n[0]))
|
||||
if n.len > 1:
|
||||
if ignoreSecond:
|
||||
result.add(n[1])
|
||||
else:
|
||||
result.add(prepareNode(cl, n[1]))
|
||||
for i in 2..<n.len:
|
||||
result.add(prepareNode(cl, n[i]))
|
||||
of nkBracketExpr:
|
||||
# don't instantiate Generic body type in expression like Generic[T]
|
||||
# exception exists for the call name being a dot expression since
|
||||
# dot expressions need their LHS instantiated
|
||||
assert n.len != 0
|
||||
let ignoreFirst = n[0].kind != nkDotExpr and
|
||||
# generic type instantiation:
|
||||
((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or
|
||||
# generic proc instantiation:
|
||||
(n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict))
|
||||
if ignoreFirst:
|
||||
result.add(n[0])
|
||||
else:
|
||||
result.add(prepareNode(cl, n[0]))
|
||||
for i in 1..<n.len:
|
||||
result.add(prepareNode(cl, n[i]))
|
||||
of nkDotExpr:
|
||||
# don't try to instantiate RHS of dot expression, it can outright be
|
||||
# undeclared, but definitely instantiate LHS
|
||||
assert n.len >= 2
|
||||
result.add(prepareNode(cl, n[0]))
|
||||
result.add(n[1])
|
||||
for i in 2..<n.len:
|
||||
result.add(prepareNode(cl, n[i]))
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
result.add(prepareNode(cl, n[i]))
|
||||
|
||||
proc isTypeParam(n: PNode): bool =
|
||||
# XXX: generic params should use skGenericParam instead of skType
|
||||
@@ -222,7 +261,10 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
|
||||
if n == nil: return
|
||||
result = copyNode(n)
|
||||
if n.typ != nil:
|
||||
result.typ = replaceTypeVarsT(cl, n.typ)
|
||||
if n.typ.kind == tyFromExpr:
|
||||
# type of node should not be evaluated as a static value
|
||||
n.typ.flags.incl tfNonConstExpr
|
||||
result.typ() = replaceTypeVarsT(cl, n.typ)
|
||||
checkMetaInvariants(cl, result.typ)
|
||||
case n.kind
|
||||
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
|
||||
@@ -234,7 +276,13 @@ 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.typ.kind == tyVoid:
|
||||
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
|
||||
result = newNodeI(nkRecList, n.info)
|
||||
of nkRecWhen:
|
||||
@@ -315,7 +363,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
|
||||
result = copySym(s, cl.c.idgen)
|
||||
incl(result.flags, sfFromGeneric)
|
||||
#idTablePut(cl.symMap, s, result)
|
||||
result.owner = s.owner
|
||||
setOwner(result, s.owner)
|
||||
result.typ = t
|
||||
if result.kind != skType:
|
||||
result.ast = replaceTypeVarsN(cl, s.ast)
|
||||
@@ -433,12 +481,12 @@ 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
|
||||
|
||||
cl.typeMap = cl.typeMap.nextLayer
|
||||
setToPreviousLayer(cl.typeMap)
|
||||
|
||||
# This is actually wrong: tgeneric_closure fails with this line:
|
||||
#newbody.callConv = body.callConv
|
||||
@@ -530,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
|
||||
@@ -573,6 +621,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
result.kind = tyUserTypeClassInst
|
||||
|
||||
of tyGenericBody:
|
||||
if cl.allowMetaTypes: return
|
||||
localError(
|
||||
cl.c.config,
|
||||
cl.info,
|
||||
@@ -591,13 +640,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
assert t.n.typ != t
|
||||
var n = prepareNode(cl, t.n)
|
||||
if n.kind != nkEmpty:
|
||||
n = cl.c.semConstExpr(cl.c, n)
|
||||
if tfNonConstExpr in t.flags:
|
||||
n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof})
|
||||
else:
|
||||
n = cl.c.semConstExpr(cl.c, n)
|
||||
if n.typ.kind == tyTypeDesc:
|
||||
# XXX: sometimes, chained typedescs enter here.
|
||||
# It may be worth investigating why this is happening,
|
||||
# because it may cause other bugs elsewhere.
|
||||
result = n.typ.skipTypes({tyTypeDesc})
|
||||
# result = n.typ.base
|
||||
elif tfNonConstExpr in t.flags:
|
||||
result = n.typ
|
||||
else:
|
||||
if n.typ.kind != tyStatic and n.kind != nkType:
|
||||
# XXX: In the future, semConstExpr should
|
||||
@@ -623,8 +677,31 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
elif t.elementType.kind != tyNone:
|
||||
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))
|
||||
|
||||
of tyUserTypeClass, tyStatic:
|
||||
of tyUserTypeClass:
|
||||
result = t
|
||||
|
||||
of tyStatic:
|
||||
if cl.c.matchedConcept != nil:
|
||||
# allow concepts to not instantiate statics for now
|
||||
# they can't always infer them
|
||||
return
|
||||
if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
|
||||
# no need to instantiate
|
||||
return
|
||||
bailout()
|
||||
result = instCopyType(cl, t)
|
||||
cl.localCache[t.itemId] = result
|
||||
for i in FirstGenericParamAt..<result.kidsLen:
|
||||
var r = result[i]
|
||||
if r != nil:
|
||||
r = replaceTypeVarsT(cl, r)
|
||||
result[i] = r
|
||||
propagateToOwner(result, r)
|
||||
result.n = replaceTypeVarsN(cl, result.n)
|
||||
if not cl.allowMetaTypes and result.n != nil and
|
||||
result.base.kind != tyNone:
|
||||
result.n = cl.c.semConstExpr(cl.c, result.n)
|
||||
result.n.typ() = result.base
|
||||
|
||||
of tyGenericInst, tyUserTypeClassInst:
|
||||
bailout()
|
||||
@@ -635,23 +712,27 @@ 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:
|
||||
if resulti.kind == tyGenericBody:
|
||||
if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
|
||||
localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
|
||||
"cannot instantiate '" &
|
||||
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})
|
||||
@@ -700,16 +781,24 @@ proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
|
||||
localCache: initTypeMapping(), typeMap: typeMap,
|
||||
info: info, c: p, owner: owner)
|
||||
|
||||
proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
|
||||
proc replaceTypesInBody*(p: PContext, pt: LayeredIdTable, n: PNode;
|
||||
owner: PSym, allowMetaTypes = false,
|
||||
fromStaticExpr = false, expectedType: PType = nil): PNode =
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
|
||||
var cl = initTypeVars(p, typeMap, n.info, owner)
|
||||
cl.allowMetaTypes = allowMetaTypes
|
||||
pushInfoContext(p.config, n.info)
|
||||
result = replaceTypeVarsN(cl, n, expectedType = expectedType)
|
||||
popInfoContext(p.config)
|
||||
|
||||
proc prepareTypesInBody*(p: PContext, pt: LayeredIdTable, n: PNode;
|
||||
owner: PSym = nil): PNode =
|
||||
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
|
||||
var cl = initTypeVars(p, typeMap, n.info, owner)
|
||||
pushInfoContext(p.config, n.info)
|
||||
result = prepareNode(cl, n)
|
||||
popInfoContext(p.config)
|
||||
|
||||
when false:
|
||||
# deadcode
|
||||
proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
|
||||
@@ -737,13 +826,13 @@ proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
|
||||
inc currPosition
|
||||
else: discard "cannot happen"
|
||||
|
||||
proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo,
|
||||
proc generateTypeInstance*(p: PContext, pt: LayeredIdTable, info: TLineInfo,
|
||||
t: PType): PType =
|
||||
# Given `t` like Foo[T]
|
||||
# pt: Table with type mappings: T -> int
|
||||
# Desired result: Foo[int]
|
||||
# proc (x: T = 0); T -> int ----> proc (x: int = 0)
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
|
||||
var cl = initTypeVars(p, typeMap, info, nil)
|
||||
pushInfoContext(p.config, info)
|
||||
result = replaceTypeVarsT(cl, t)
|
||||
@@ -753,15 +842,15 @@ proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo,
|
||||
var position = 0
|
||||
recomputeFieldPositions(objType, objType.n, position)
|
||||
|
||||
proc prepareMetatypeForSigmatch*(p: PContext, pt: TypeMapping, info: TLineInfo,
|
||||
proc prepareMetatypeForSigmatch*(p: PContext, pt: LayeredIdTable, info: TLineInfo,
|
||||
t: PType): PType =
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
var typeMap = shallowCopy(pt) # use previous bindings without writing to them
|
||||
var cl = initTypeVars(p, typeMap, info, nil)
|
||||
cl.allowMetaTypes = true
|
||||
pushInfoContext(p.config, info)
|
||||
result = replaceTypeVarsT(cl, t)
|
||||
popInfoContext(p.config)
|
||||
|
||||
template generateTypeInstance*(p: PContext, pt: TypeMapping, arg: PNode,
|
||||
template generateTypeInstance*(p: PContext, pt: LayeredIdTable, arg: PNode,
|
||||
t: PType): untyped =
|
||||
generateTypeInstance(p, pt, arg.info, t)
|
||||
|
||||
@@ -55,6 +55,8 @@ proc hashSym(c: var MD5Context, s: PSym) =
|
||||
c &= it.name.s
|
||||
c &= "."
|
||||
it = it.owner
|
||||
c &= "#"
|
||||
c &= s.disamb
|
||||
|
||||
proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
|
||||
if sfAnon in s.flags or s.kind == skGenericParam:
|
||||
@@ -69,6 +71,8 @@ proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
|
||||
c &= it.name.s
|
||||
c &= "."
|
||||
it = it.owner
|
||||
c &= "#"
|
||||
c &= s.disamb
|
||||
|
||||
proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) =
|
||||
if n == nil:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import
|
||||
ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
|
||||
magicsys, idents, lexer, options, parampatterns, trees,
|
||||
linter, lineinfos, lowerings, modulegraphs, concepts
|
||||
linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable
|
||||
|
||||
import std/[intsets, strutils, tables]
|
||||
|
||||
@@ -23,7 +23,8 @@ when defined(nimPreviewSlimSystem):
|
||||
type
|
||||
MismatchKind* = enum
|
||||
kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
|
||||
kMissingParam, kExtraArg, kPositionalAlreadyGiven
|
||||
kMissingParam, kExtraArg, kPositionalAlreadyGiven,
|
||||
kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam
|
||||
|
||||
MismatchInfo* = object
|
||||
kind*: MismatchKind # reason for mismatch
|
||||
@@ -55,12 +56,12 @@ type
|
||||
calleeScope*: int # scope depth:
|
||||
# is this a top-level symbol or a nested proc?
|
||||
call*: PNode # modified call
|
||||
bindings*: TypeMapping # maps types to types
|
||||
bindings*: LayeredIdTable # maps types to types
|
||||
magic*: TMagic # magic of operation
|
||||
baseTypeMatch: bool # needed for conversions from T to openarray[T]
|
||||
# for example
|
||||
fauxMatch*: TTypeKind # the match was successful only due to the use
|
||||
# of error or wildcard (unknown) types.
|
||||
matchedErrorType*: bool # match is considered successful after matching
|
||||
# error type to avoid cascading errors
|
||||
# this is used to prevent instantiations.
|
||||
genericConverter*: bool # true if a generic converter needs to
|
||||
# be instantiated
|
||||
@@ -84,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
|
||||
@@ -101,8 +105,6 @@ const
|
||||
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
|
||||
proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
|
||||
|
||||
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
|
||||
|
||||
proc initCandidateAux(ctx: PContext,
|
||||
callee: PType): TCandidate {.inline.} =
|
||||
result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0,
|
||||
@@ -115,21 +117,128 @@ proc initCandidateAux(ctx: PContext,
|
||||
proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||
result = initCandidateAux(ctx, callee)
|
||||
result.calleeSym = nil
|
||||
result.bindings = initTypeMapping()
|
||||
result.bindings = initLayeredTypeMap()
|
||||
|
||||
proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
||||
## Given: proc foo[T](x: T); foo(4)
|
||||
## key: 'T'
|
||||
## val: 'int' (typeof(4))
|
||||
when false:
|
||||
let old = idTableGet(c.bindings, key)
|
||||
let old = lookup(c.bindings, key)
|
||||
if old != nil:
|
||||
echo "Putting ", typeToString(key), " ", typeToString(val), " and old is ", typeToString(old)
|
||||
if typeToString(old) == "float32":
|
||||
writeStackTrace()
|
||||
if c.c.module.name.s == "temp3":
|
||||
echo "binding ", key, " -> ", val
|
||||
idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen))
|
||||
put(c.bindings, key, val.skipIntLit(c.c.idgen))
|
||||
|
||||
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation
|
||||
|
||||
proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
|
||||
var arg = n.typ
|
||||
if m.c.inGenericContext > 0:
|
||||
# don't match yet-unresolved generic instantiations
|
||||
while arg != nil and arg.kind == tyGenericParam:
|
||||
arg = lookup(m.bindings, arg)
|
||||
if arg == nil or arg.containsUnresolvedType:
|
||||
m.state = csNoMatch
|
||||
return
|
||||
# fix up the type to get ready to match formal:
|
||||
var formalBase = formal
|
||||
while formalBase.kind == tyGenericParam and
|
||||
formalBase.genericParamHasConstraints:
|
||||
formalBase = formalBase.genericConstraint
|
||||
if formalBase.kind == tyStatic and arg.kind != tyStatic:
|
||||
# maybe call `paramTypesMatch` here, for now be conservative
|
||||
if n.kind in nkSymChoices: n.flags.excl nfSem
|
||||
let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic}))
|
||||
if evaluated != nil:
|
||||
arg = newTypeS(tyStatic, m.c, son = evaluated.typ)
|
||||
arg.n = evaluated
|
||||
elif formalBase.kind == tyTypeDesc:
|
||||
if arg.kind != tyTypeDesc:
|
||||
arg = makeTypeDesc(m.c, arg)
|
||||
else:
|
||||
arg = arg.skipTypes({tyTypeDesc})
|
||||
let tm = typeRel(m, formal, arg)
|
||||
if tm in {isNone, isConvertible}:
|
||||
m.state = csNoMatch
|
||||
m.firstMismatch.kind = kGenericParamTypeMismatch
|
||||
return
|
||||
|
||||
proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) =
|
||||
## matches explicit generic instantiation `binding` against generic params of
|
||||
## proc symbol `callee`
|
||||
## state is set to `csMatch` if all generic params match, `csEmpty` if
|
||||
## implicit generic parameters are missing (matches but cannot instantiate),
|
||||
## `csNoMatch` if a constraint fails or param count doesn't match
|
||||
let c = m.c
|
||||
let typeParams = callee.ast[genericParamsPos]
|
||||
let paramCount = typeParams.len
|
||||
let bindingCount = binding.len-1
|
||||
if bindingCount > paramCount:
|
||||
m.state = csNoMatch
|
||||
m.firstMismatch.kind = kExtraGenericParam
|
||||
m.firstMismatch.arg = paramCount + 1
|
||||
return
|
||||
for i in 1..bindingCount:
|
||||
matchGenericParam(m, typeParams[i-1].typ, binding[i])
|
||||
if m.state == csNoMatch:
|
||||
m.firstMismatch.arg = i
|
||||
m.firstMismatch.formal = typeParams[i-1].sym
|
||||
return
|
||||
# not enough generic params given, check if remaining have defaults:
|
||||
for i in bindingCount ..< paramCount:
|
||||
let param = typeParams[i]
|
||||
assert param.kind == nkSym
|
||||
let paramSym = param.sym
|
||||
if paramSym.ast != nil:
|
||||
matchGenericParam(m, param.typ, paramSym.ast)
|
||||
if m.state == csNoMatch:
|
||||
m.firstMismatch.arg = i + 1
|
||||
m.firstMismatch.formal = paramSym
|
||||
return
|
||||
elif tfImplicitTypeParam in paramSym.typ.flags:
|
||||
# not a mismatch, but can't create sym
|
||||
m.state = csEmpty
|
||||
m.firstMismatch.kind = kMissingGenericParam
|
||||
m.firstMismatch.arg = i + 1
|
||||
m.firstMismatch.formal = paramSym
|
||||
return
|
||||
else:
|
||||
m.state = csNoMatch
|
||||
m.firstMismatch.kind = kMissingGenericParam
|
||||
m.firstMismatch.arg = i + 1
|
||||
m.firstMismatch.formal = paramSym
|
||||
return
|
||||
m.state = csMatch
|
||||
|
||||
proc copyingEraseVoidParams(m: TCandidate, t: var PType) =
|
||||
## if `t` is a proc type with void parameters, copies it and erases them
|
||||
assert t.kind == tyProc
|
||||
let original = t
|
||||
var copied = false
|
||||
for i in 1 ..< original.len:
|
||||
var f = original[i]
|
||||
var isVoidParam = f.kind == tyVoid
|
||||
if not isVoidParam:
|
||||
let prev = lookup(m.bindings, f)
|
||||
if prev != nil: f = prev
|
||||
isVoidParam = f.kind == tyVoid
|
||||
if isVoidParam:
|
||||
if not copied:
|
||||
# keep first i children
|
||||
t = copyType(original, m.c.idgen, t.owner)
|
||||
t.setSonsLen(i)
|
||||
t.n = copyNode(original.n)
|
||||
t.n.sons = original.n.sons
|
||||
t.n.sons.setLen(i)
|
||||
copied = true
|
||||
elif copied:
|
||||
t.add(f)
|
||||
t.n.add(original.n[i])
|
||||
|
||||
proc initCandidate*(ctx: PContext, callee: PSym,
|
||||
binding: PNode, calleeScope = -1,
|
||||
@@ -143,19 +252,22 @@ proc initCandidate*(ctx: PContext, callee: PSym,
|
||||
result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
|
||||
result.diagnosticsEnabled = diagnosticsEnabled
|
||||
result.magic = result.calleeSym.magic
|
||||
result.bindings = initTypeMapping()
|
||||
result.bindings = initLayeredTypeMap()
|
||||
if binding != nil and callee.kind in routineKinds:
|
||||
var typeParams = callee.ast[genericParamsPos]
|
||||
for i in 1..min(typeParams.len, binding.len-1):
|
||||
var formalTypeParam = typeParams[i-1].typ
|
||||
var bound = binding[i].typ
|
||||
if bound != nil:
|
||||
if formalTypeParam.kind == tyTypeDesc:
|
||||
if bound.kind != tyTypeDesc:
|
||||
bound = makeTypeDesc(ctx, bound)
|
||||
else:
|
||||
bound = bound.skipTypes({tyTypeDesc})
|
||||
put(result, formalTypeParam, bound)
|
||||
matchGenericParams(result, binding, callee)
|
||||
let genericMatch = result.state
|
||||
if genericMatch != csNoMatch:
|
||||
result.state = csEmpty
|
||||
if genericMatch == csMatch: # csEmpty if not fully instantiated
|
||||
# instantiate the type, emulates old compiler behavior
|
||||
# wouldn't be needed if sigmatch could handle complex cases,
|
||||
# examples are in texplicitgenerics
|
||||
# might be buggy, see rest of generateInstance if problems occur
|
||||
let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info)
|
||||
result.callee = typ
|
||||
else:
|
||||
# createThread[void] requires this if the above branch is removed:
|
||||
copyingEraseVoidParams(result, result.callee)
|
||||
|
||||
proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
binding: PNode, calleeScope = -1): TCandidate =
|
||||
@@ -164,7 +276,7 @@ proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||
result = initCandidate(ctx, callee)
|
||||
|
||||
proc copyCandidate(dest: var TCandidate, src: TCandidate) =
|
||||
proc shallowCopyCandidate(dest: var TCandidate, src: TCandidate) =
|
||||
dest.c = src.c
|
||||
dest.exactMatches = src.exactMatches
|
||||
dest.subtypeMatches = src.subtypeMatches
|
||||
@@ -176,10 +288,7 @@ proc copyCandidate(dest: var TCandidate, src: TCandidate) =
|
||||
dest.calleeSym = src.calleeSym
|
||||
dest.call = copyTree(src.call)
|
||||
dest.baseTypeMatch = src.baseTypeMatch
|
||||
dest.bindings = src.bindings
|
||||
|
||||
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation
|
||||
dest.bindings = shallowCopy(src.bindings)
|
||||
|
||||
proc checkGeneric(a, b: TCandidate): int =
|
||||
let c = a.c
|
||||
@@ -338,16 +447,27 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p
|
||||
result.add renderTree(n[i][0])
|
||||
result.add ": "
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
|
||||
# XXX we really need to 'tryExpr' here!
|
||||
arg = c.semOperand(c, n[i][1])
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
arg = c.semTryExpr(c, n[i][1])
|
||||
if arg == nil:
|
||||
arg = n[i][1]
|
||||
arg.typ() = newTypeS(tyUntyped, c)
|
||||
else:
|
||||
if arg.typ == nil:
|
||||
arg.typ() = newTypeS(tyVoid, c)
|
||||
n[i].typ() = arg.typ
|
||||
n[i][1] = arg
|
||||
else:
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
|
||||
nkOfBranch, nkElifBranch,
|
||||
nkExceptBranch}:
|
||||
arg = c.semOperand(c, n[i])
|
||||
n[i] = arg
|
||||
arg = c.semTryExpr(c, n[i])
|
||||
if arg == nil:
|
||||
arg = n[i]
|
||||
arg.typ() = newTypeS(tyUntyped, c)
|
||||
else:
|
||||
if arg.typ == nil:
|
||||
arg.typ() = newTypeS(tyVoid, c)
|
||||
n[i] = arg
|
||||
if arg.typ != nil and arg.typ.kind == tyError: return
|
||||
result.add argTypeToString(arg, prefer)
|
||||
|
||||
@@ -371,8 +491,9 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
|
||||
else: result = t
|
||||
of tyGenericParam, tyAnything, tyConcept:
|
||||
result = t
|
||||
if c.isNoCall: return
|
||||
while true:
|
||||
result = idTableGet(c.bindings, t)
|
||||
result = lookup(c.bindings, t)
|
||||
if result == nil:
|
||||
break # it's ok, no match
|
||||
# example code that triggers it:
|
||||
@@ -398,9 +519,16 @@ proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation =
|
||||
let k = ab.kind
|
||||
let nf = c.config.normalizeKind(f.kind)
|
||||
let na = c.config.normalizeKind(k)
|
||||
if k == f.kind: result = isSubrange
|
||||
elif k == tyInt and f.kind in {tyRange, tyInt..tyInt64,
|
||||
tyUInt..tyUInt64} and
|
||||
if k == f.kind:
|
||||
# `a` is a range type matching its base type
|
||||
# see very bottom for range types matching different types
|
||||
if isIntLit(ab):
|
||||
# range type can only give isFromIntLit for base type
|
||||
result = isFromIntLit
|
||||
else:
|
||||
result = isSubrange
|
||||
elif a.kind == tyInt and f.kind in {tyRange, tyInt..tyInt64,
|
||||
tyUInt..tyUInt64} and
|
||||
isIntLit(ab) and getInt(ab.n) >= firstOrd(nil, f) and
|
||||
getInt(ab.n) <= lastOrd(nil, f):
|
||||
# passing 'nil' to firstOrd/lastOrd here as type checking rules should
|
||||
@@ -510,16 +638,26 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if fGenericOrigin != nil and last.kind == tyGenericInst and
|
||||
last.kidsLen-1 == fGenericOrigin.kidsLen:
|
||||
for i in FirstGenericParamAt..<fGenericOrigin.kidsLen:
|
||||
let x = idTableGet(c.bindings, fGenericOrigin[i])
|
||||
let x = lookup(c.bindings, fGenericOrigin[i])
|
||||
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
|
||||
@@ -648,7 +786,7 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
|
||||
a = a
|
||||
|
||||
if a.isMetaType:
|
||||
let aResolved = idTableGet(c.bindings, a)
|
||||
let aResolved = lookup(c.bindings, a)
|
||||
if aResolved != nil:
|
||||
a = aResolved
|
||||
if a.isMetaType:
|
||||
@@ -686,6 +824,8 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
|
||||
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
case a.kind
|
||||
of tyProc:
|
||||
var f = f
|
||||
copyingEraseVoidParams(c, f)
|
||||
if f.signatureLen != a.signatureLen: return
|
||||
result = isEqual # start with maximum; also correct for no
|
||||
# params at all
|
||||
@@ -772,7 +912,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||
typeParamName = ff.base[i-1].sym.name
|
||||
typ = ff[i]
|
||||
param: PSym = nil
|
||||
alreadyBound = idTableGet(m.bindings, typ)
|
||||
alreadyBound = lookup(m.bindings, typ)
|
||||
|
||||
if alreadyBound != nil: typ = alreadyBound
|
||||
|
||||
@@ -795,7 +935,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||
param.typ.flags.incl tfInferrableStatic
|
||||
else:
|
||||
param.ast = typ.n
|
||||
of tyUnknown:
|
||||
of tyFromExpr:
|
||||
param = paramSym skVar
|
||||
param.typ = typ.exactReplica
|
||||
#copyType(typ, c.idgen, typ.owner)
|
||||
@@ -874,6 +1014,7 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
|
||||
|
||||
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
|
||||
allowUnresolved = false,
|
||||
allowCalls = false,
|
||||
expectedType: PType = nil): PNode =
|
||||
# Consider this example:
|
||||
# type Value[N: static[int]] = object
|
||||
@@ -883,7 +1024,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
|
||||
# This proc is used to evaluate such static expressions.
|
||||
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
|
||||
allowMetaTypes = allowUnresolved)
|
||||
if instantiated.kind in nkCallKinds:
|
||||
if not allowCalls and instantiated.kind in nkCallKinds:
|
||||
return nil
|
||||
result = c.c.semExpr(c.c, instantiated)
|
||||
|
||||
@@ -955,7 +1096,8 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
|
||||
|
||||
else: discard
|
||||
|
||||
elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
|
||||
elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and
|
||||
(lhs.typ.n == nil or lookup(c.bindings, lhs.typ) == nil):
|
||||
var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType)
|
||||
inferred.n = newIntNode(nkIntLit, rhs)
|
||||
put(c, lhs.typ, inferred)
|
||||
@@ -999,9 +1141,21 @@ proc inferStaticsInRange(c: var TCandidate,
|
||||
doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
|
||||
|
||||
template subtypeCheck() =
|
||||
if result <= isSubrange and f.last.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
case result
|
||||
of isIntConv:
|
||||
result = isNone
|
||||
of isSubrange:
|
||||
discard # XXX should be isNone with preview define, warnings
|
||||
of isConvertible:
|
||||
if f.last.skipTypes(abstractInst).kind != tyOpenArray:
|
||||
# exclude var openarray which compiler supports
|
||||
result = isNone
|
||||
of isSubtype:
|
||||
if f.last.skipTypes(abstractInst).kind in {
|
||||
tyRef, tyPtr, tyVar, tyLent, tyOwned}:
|
||||
# compiler can't handle subtype conversions with pointer indirection
|
||||
result = isNone
|
||||
else: discard
|
||||
|
||||
proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
# this proc is always called for a pair of matching types
|
||||
@@ -1099,7 +1253,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
|
||||
case f.kind
|
||||
of tyGenericParam:
|
||||
var prev = idTableGet(c.bindings, f)
|
||||
var prev = lookup(c.bindings, f)
|
||||
if prev != nil: candidate = prev
|
||||
of tyFromExpr:
|
||||
let computedType = tryResolvingStaticExpr(c, f.n).typ
|
||||
@@ -1149,10 +1303,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
return res
|
||||
|
||||
template considerPreviousT(body: untyped) =
|
||||
var prev = idTableGet(c.bindings, f)
|
||||
var prev = lookup(c.bindings, f)
|
||||
if prev == nil: body
|
||||
else: return typeRel(c, prev, a, flags)
|
||||
|
||||
if c.c.inGenericContext > 0 and not c.isNoCall and
|
||||
(tfUnresolved in a.flags or a.kind in tyTypeClasses):
|
||||
# cheap check for unresolved arg, not nested
|
||||
return isNone
|
||||
|
||||
case a.kind
|
||||
of tyOr:
|
||||
# XXX: deal with the current dual meaning of tyGenericParam
|
||||
@@ -1278,7 +1437,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
var fRange = f.indexType
|
||||
var aRange = a.indexType
|
||||
if fRange.kind in {tyGenericParam, tyAnything}:
|
||||
var prev = idTableGet(c.bindings, fRange)
|
||||
var prev = lookup(c.bindings, fRange)
|
||||
if prev == nil:
|
||||
if typeRel(c, fRange, aRange) == isNone:
|
||||
return isNone
|
||||
@@ -1397,7 +1556,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
reduceToBase(a)
|
||||
if effectiveArgType.kind == tyObject:
|
||||
if sameObjectTypes(f, effectiveArgType):
|
||||
c.inheritancePenalty = 0
|
||||
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
|
||||
result = isEqual
|
||||
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
|
||||
elif trIsOutParam notin flags:
|
||||
@@ -1418,11 +1577,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
result = typeRel(c, f[0], a[0], flags)
|
||||
if result < isGeneric:
|
||||
if result <= isConvertible:
|
||||
result = isNone
|
||||
elif tfIsConstructor notin a.flags:
|
||||
# set constructors are a bit special...
|
||||
if tfIsConstructor notin a.flags:
|
||||
# set['a'..'z'] and set[char] have different representations
|
||||
result = isNone
|
||||
else:
|
||||
# but we can convert individual elements of the constructor
|
||||
result = isConvertible
|
||||
of tyPtr, tyRef:
|
||||
a = reduceToBase(a)
|
||||
if a.kind == f.kind:
|
||||
@@ -1511,7 +1671,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
result = isNone
|
||||
of tyGenericInst:
|
||||
var prev = idTableGet(c.bindings, f)
|
||||
var prev = lookup(c.bindings, f)
|
||||
let origF = f
|
||||
var f = if prev == nil: f else: prev
|
||||
|
||||
@@ -1643,14 +1803,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
#
|
||||
# we steal the generic parameters from the tyGenericBody:
|
||||
for i in 1..<f.len:
|
||||
let x = idTableGet(c.bindings, genericBody[i-1])
|
||||
let x = lookup(c.bindings, genericBody[i-1])
|
||||
if x == nil:
|
||||
discard "maybe fine (for e.g. a==tyNil)"
|
||||
elif x.kind in {tyGenericInvocation, tyGenericParam}:
|
||||
internalError(c.c.graph.config, "wrong instantiated type!")
|
||||
else:
|
||||
let key = f[i]
|
||||
let old = idTableGet(c.bindings, key)
|
||||
let old = lookup(c.bindings, key)
|
||||
if old == nil:
|
||||
put(c, key, x)
|
||||
elif typeRel(c, old, x, flags + {trDontBind}) == isNone:
|
||||
@@ -1718,9 +1878,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}:
|
||||
@@ -1755,8 +1918,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
result = isNone
|
||||
of tyConcept:
|
||||
result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric
|
||||
else: isNone
|
||||
if a.kind == tyConcept and sameType(f, a):
|
||||
result = isGeneric
|
||||
else:
|
||||
result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric
|
||||
else: isNone
|
||||
of tyCompositeTypeClass:
|
||||
considerPreviousT:
|
||||
let roota = a.skipGenericAlias
|
||||
@@ -1775,7 +1941,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
result = isGeneric
|
||||
of tyGenericParam:
|
||||
let doBindGP = doBind or trBindGenericParam in flags
|
||||
var x = idTableGet(c.bindings, f)
|
||||
var x = lookup(c.bindings, f)
|
||||
if x == nil:
|
||||
if c.callee.kind == tyGenericBody and not c.typedescMatched:
|
||||
# XXX: The fact that generic types currently use tyGenericParam for
|
||||
@@ -1855,13 +2021,22 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
c.inheritancePenalty = inheritancePenaltyOld
|
||||
if result > isGeneric: result = isGeneric
|
||||
of tyStatic:
|
||||
let prev = idTableGet(c.bindings, f)
|
||||
let prev = lookup(c.bindings, f)
|
||||
if prev == nil:
|
||||
if aOrig.kind == tyStatic:
|
||||
if f.base.kind notin {tyNone, tyGenericParam}:
|
||||
if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall:
|
||||
# don't match unresolved static value to static param to avoid
|
||||
# faulty instantiations in calls in generic bodies
|
||||
# but not for generic invocations as they only check constraints
|
||||
result = isNone
|
||||
elif f.base.kind notin {tyNone, tyGenericParam}:
|
||||
result = typeRel(c, f.base, a, flags)
|
||||
if result != isNone and f.n != nil:
|
||||
if not exprStructuralEquivalent(f.n, aOrig.n):
|
||||
var r = tryResolvingStaticExpr(c, f.n)
|
||||
if r == nil: r = f.n
|
||||
if not exprStructuralEquivalent(r, aOrig.n) and
|
||||
not (aOrig.n != nil and aOrig.n.kind == nkIntLit and
|
||||
inferStaticParam(c, r, aOrig.n.intVal)):
|
||||
result = isNone
|
||||
elif f.base.kind == tyGenericParam:
|
||||
# Handling things like `type A[T; Y: static T] = object`
|
||||
@@ -1908,13 +2083,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
c.inferredTypes.add f
|
||||
f.add a
|
||||
of tyTypeDesc:
|
||||
var prev = idTableGet(c.bindings, f)
|
||||
var prev = lookup(c.bindings, f)
|
||||
if prev == nil:
|
||||
# proc foo(T: typedesc, x: T)
|
||||
# when `f` is an unresolved typedesc, `a` could be any
|
||||
# type, so we should not perform this check earlier
|
||||
if c.c.inGenericContext > 0 and
|
||||
a.skipTypes({tyTypeDesc}).kind == tyGenericParam:
|
||||
if c.c.inGenericContext > 0 and a.containsUnresolvedType:
|
||||
# generic type bodies can sometimes compile call expressions
|
||||
# prevent unresolved generic parameters from being passed to procs as
|
||||
# typedesc parameters
|
||||
@@ -1943,28 +2117,36 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if aOrig != nil:
|
||||
put(c, f, aOrig)
|
||||
result = isGeneric
|
||||
of tyProxy:
|
||||
of tyError:
|
||||
result = isEqual
|
||||
of tyFromExpr:
|
||||
# fix the expression, so it contains the already instantiated types
|
||||
if f.n == nil or f.n.kind == nkEmpty: return isGeneric
|
||||
let reevaluated = tryResolvingStaticExpr(c, f.n)
|
||||
if reevaluated == nil:
|
||||
if c.c.inGenericContext > 0:
|
||||
# need to delay until instantiation
|
||||
# also prevent infinite recursion below
|
||||
return isNone
|
||||
inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
|
||||
# use prepareNode for consistency with other tyFromExpr in semtypinst:
|
||||
let instantiated = prepareTypesInBody(c.c, c.bindings, f.n)
|
||||
let reevaluated = c.c.semExpr(c.c, instantiated).typ
|
||||
dec c.c.inGenericContext
|
||||
case reevaluated.kind
|
||||
of tyFromExpr:
|
||||
# not resolved
|
||||
result = isNone
|
||||
return
|
||||
case reevaluated.typ.kind
|
||||
of tyTypeDesc:
|
||||
result = typeRel(c, a, reevaluated.typ.base, flags)
|
||||
result = typeRel(c, reevaluated.base, a, flags)
|
||||
of tyStatic:
|
||||
result = typeRel(c, a, reevaluated.typ.base, flags)
|
||||
if result != isNone and reevaluated.typ.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
|
||||
result = typeRel(c, reevaluated.base, a, flags)
|
||||
if result != isNone and reevaluated.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
|
||||
result = isNone
|
||||
else:
|
||||
# bug #14136: other types are just like 'tyStatic' here:
|
||||
result = typeRel(c, a, reevaluated.typ, flags)
|
||||
if result != isNone and reevaluated.typ.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
|
||||
result = typeRel(c, reevaluated, a, flags)
|
||||
if result != isNone and reevaluated.n != nil:
|
||||
if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
|
||||
result = isNone
|
||||
of tyNone:
|
||||
if a.kind == tyNone: result = isEqual
|
||||
@@ -1992,7 +2174,7 @@ proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||
|
||||
proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
|
||||
f: PType): PType =
|
||||
result = idTableGet(m.bindings, f)
|
||||
result = lookup(m.bindings, f)
|
||||
if result == nil:
|
||||
result = generateTypeInstance(c, m.bindings, arg, f)
|
||||
if result == nil:
|
||||
@@ -2003,17 +2185,17 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
|
||||
c: PContext): PNode =
|
||||
result = newNodeI(kind, arg.info)
|
||||
if containsGenericType(f):
|
||||
if not m.hasFauxMatch:
|
||||
result.typ = getInstantiatedType(c, arg, m, f).skipTypes({tySink})
|
||||
if not m.matchedErrorType:
|
||||
result.typ() = getInstantiatedType(c, arg, m, f).skipTypes({tySink})
|
||||
else:
|
||||
result.typ = errorType(c)
|
||||
result.typ() = errorType(c)
|
||||
else:
|
||||
result.typ = f.skipTypes({tySink})
|
||||
result.typ() = f.skipTypes({tySink})
|
||||
# keep varness
|
||||
if arg.typ != nil and arg.typ.kind == tyVar:
|
||||
result.typ = toVar(result.typ, tyVar, c.idgen)
|
||||
result.typ() = toVar(result.typ, tyVar, c.idgen)
|
||||
else:
|
||||
result.typ = result.typ.skipTypes({tyVar})
|
||||
result.typ() = result.typ.skipTypes({tyVar})
|
||||
|
||||
if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
|
||||
result.add c.graph.emptyNode
|
||||
@@ -2024,6 +2206,81 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
|
||||
else:
|
||||
result.add arg
|
||||
|
||||
proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode =
|
||||
# based off changeType but generates implicit conversions instead
|
||||
template addConsiderNil(s, node) =
|
||||
let val = node
|
||||
if val.isNil: return nil
|
||||
s.add(val)
|
||||
case n.kind
|
||||
of nkCurly:
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
if n[i].kind == nkRange:
|
||||
var x = copyNode(n[i])
|
||||
x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType))
|
||||
x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType))
|
||||
result.add x
|
||||
else:
|
||||
result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
|
||||
result.typ() = newType
|
||||
return
|
||||
of nkBracket:
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
|
||||
result.typ() = newType
|
||||
return
|
||||
of nkPar, nkTupleConstr:
|
||||
let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if tup.kind == tyTuple:
|
||||
result = copyNode(n)
|
||||
if n.len > 0 and n[0].kind == nkExprColonExpr:
|
||||
# named tuple?
|
||||
for i in 0..<n.len:
|
||||
var name = n[i][0]
|
||||
if name.kind != nkSym:
|
||||
#globalError(c.config, name.info, "invalid tuple constructor")
|
||||
return nil
|
||||
if tup.n != nil:
|
||||
var f = getSymFromList(tup.n, name.sym.name)
|
||||
if f == nil:
|
||||
#globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s)
|
||||
return nil
|
||||
result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ)
|
||||
else:
|
||||
result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i])
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i])
|
||||
result.typ() = newType
|
||||
return
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType):
|
||||
let value = n.intVal
|
||||
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
|
||||
return nil
|
||||
result = copyNode(n)
|
||||
result.typ() = newType
|
||||
return
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat:
|
||||
if not floatRangeCheck(n.floatVal, newType):
|
||||
return nil
|
||||
result = copyNode(n)
|
||||
result.typ() = newType
|
||||
return
|
||||
of nkSym:
|
||||
if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType):
|
||||
let value = n.sym.position
|
||||
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
|
||||
return nil
|
||||
result = copyNode(n)
|
||||
result.typ() = newType
|
||||
return
|
||||
else: discard
|
||||
return implicitConv(kind, newType, n, m, c)
|
||||
|
||||
proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} =
|
||||
let aa = isAssignable(nil, n)
|
||||
case aa
|
||||
@@ -2033,7 +2290,7 @@ proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} =
|
||||
result = c.inUncheckedAssignSection > 0
|
||||
of arAddressableConst:
|
||||
let sym = getRoot(n)
|
||||
result = strictDefs in c.features and sym != nil and sym.kind == skLet and isOutParam
|
||||
result = noStrictDefs notin c.config.legacyFeatures and sym != nil and sym.kind == skLet and isOutParam
|
||||
else:
|
||||
result = false
|
||||
|
||||
@@ -2061,9 +2318,11 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
dest = generateTypeInstance(c, m.bindings, arg, dest)
|
||||
let fdest = typeRel(m, f, dest)
|
||||
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
|
||||
markUsed(c, arg.info, c.converters[i])
|
||||
# can't fully mark used yet, may not be used in final call
|
||||
incl(c.converters[i].flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, c.converters[i])
|
||||
var s = newSymNode(c.converters[i])
|
||||
s.typ = c.converters[i].typ
|
||||
s.typ() = c.converters[i].typ
|
||||
s.info = arg.info
|
||||
result = newNodeIT(nkHiddenCallConv, arg.info, dest)
|
||||
result.add s
|
||||
@@ -2116,7 +2375,7 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
if result.kind == nkCall: result.transitionSonsKind(nkHiddenCallConv)
|
||||
inc(m.convMatches)
|
||||
if r == isGeneric:
|
||||
result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||
result.typ() = getInstantiatedType(c, arg, m, base(f))
|
||||
m.baseTypeMatch = true
|
||||
|
||||
proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
|
||||
@@ -2161,6 +2420,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
a.n == nil and
|
||||
tfGenericTypeParam notin a.flags:
|
||||
return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
|
||||
elif a.kind == tyFromExpr and c.inGenericContext > 0:
|
||||
# don't try to evaluate
|
||||
discard
|
||||
elif arg.kind != nkEmpty:
|
||||
var evaluated = c.semTryConstExpr(c, arg)
|
||||
if evaluated != nil:
|
||||
@@ -2169,7 +2431,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
let typ = newTypeS(tyStatic, c, son = evaluated.typ)
|
||||
typ.n = evaluated
|
||||
arg = copyTree(arg) # fix #12864
|
||||
arg.typ = typ
|
||||
arg.typ() = typ
|
||||
a = typ
|
||||
else:
|
||||
if m.callee.kind == tyGenericBody:
|
||||
@@ -2209,9 +2471,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
var instantiationCounter = 0
|
||||
var lastBindingCount = -1
|
||||
while r in {isBothMetaConvertible, isInferred, isInferredConvertible} and
|
||||
lastBindingCount != m.bindings.len and
|
||||
lastBindingCount != m.bindings.currentLen and
|
||||
instantiationCounter < 100:
|
||||
lastBindingCount = m.bindings.len
|
||||
lastBindingCount = m.bindings.currentLen
|
||||
inc(instantiationCounter)
|
||||
if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
|
||||
result = c.semInferredLambda(c, m.bindings, arg)
|
||||
@@ -2232,7 +2494,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}:
|
||||
inc(m.convMatches)
|
||||
inc(m.convMatches)
|
||||
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
|
||||
if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet:
|
||||
if tfIsConstructor in a.flags and arg.kind == nkCurly:
|
||||
# we marked the set as convertible only because the arg is a literal
|
||||
# in which case we individually convert each element
|
||||
let t =
|
||||
if containsGenericType(f):
|
||||
getInstantiatedType(c, arg, m, f).skipTypes({tySink})
|
||||
else:
|
||||
f.skipTypes({tySink})
|
||||
result = convertLiteral(nkHiddenStdConv, c, m, arg, t)
|
||||
else:
|
||||
result = nil
|
||||
else:
|
||||
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
|
||||
of isIntConv:
|
||||
# I'm too lazy to introduce another ``*matches`` field, so we conflate
|
||||
# ``isIntConv`` and ``isIntLit`` here:
|
||||
@@ -2267,9 +2542,15 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
result = arg
|
||||
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
elif arg.typ.isEmptyContainer:
|
||||
elif arg.typ.isEmptyContainer or
|
||||
# XXX `and not m.isNoCall` is a workaround
|
||||
# passing an int to generic types converts it to the type `int`
|
||||
# but this isn't done for int literal types, so we preserve the type
|
||||
# i.e. works: `type Foo[T] = array[T, int]; var x: Foo[3]` (see t12938, t14193)
|
||||
# doesn't work: `proc foo[T](): array[T, int] = ...; foo[3]()` (see #23204)
|
||||
(arg.typ.isIntLit and not m.isNoCall):
|
||||
result = arg.copyTree
|
||||
result.typ = getInstantiatedType(c, arg, m, f)
|
||||
result.typ() = getInstantiatedType(c, arg, m, f).skipTypes({tySink})
|
||||
else:
|
||||
result = arg
|
||||
of isBothMetaConvertible:
|
||||
@@ -2291,13 +2572,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
|
||||
of isNone:
|
||||
# do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
|
||||
if a.kind in {tyProxy, tyUnknown}:
|
||||
if a.kind == tyUnknown and c.inGenericContext > 0:
|
||||
# don't bother with fauxMatch mechanism in generic type,
|
||||
# reject match, typechecking will be delayed to instantiation
|
||||
return nil
|
||||
if a.kind == tyFromExpr: return nil
|
||||
elif a.kind == tyError:
|
||||
inc(m.genericMatches)
|
||||
m.fauxMatch = a.kind
|
||||
m.matchedErrorType = true
|
||||
return arg
|
||||
elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
|
||||
# lift do blocks without params to lambdas
|
||||
@@ -2328,7 +2606,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
of isGeneric:
|
||||
inc(m.convMatches)
|
||||
result = copyTree(arg)
|
||||
result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||
result.typ() = getInstantiatedType(c, arg, m, base(f))
|
||||
m.baseTypeMatch = true
|
||||
of isFromIntLit:
|
||||
inc(m.intConvMatches, 256)
|
||||
@@ -2354,7 +2632,7 @@ proc staticAwareTypeRel(m: var TCandidate, f: PType, arg: var PNode): TTypeRelat
|
||||
# The ast of the type does not point to the symbol.
|
||||
# Without this we will never resolve a `static proc` with overloads
|
||||
let copiedNode = copyNode(arg)
|
||||
copiedNode.typ = exactReplica(copiedNode.typ)
|
||||
copiedNode.typ() = exactReplica(copiedNode.typ)
|
||||
copiedNode.typ.n = arg
|
||||
arg = copiedNode
|
||||
typeRel(m, f, arg.typ)
|
||||
@@ -2366,7 +2644,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
result = paramTypesMatchAux(m, f, a, arg, argOrig)
|
||||
else:
|
||||
# symbol kinds that don't participate in symchoice type disambiguation:
|
||||
let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage, skType}
|
||||
let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
var best = -1
|
||||
result = arg
|
||||
@@ -2409,8 +2687,15 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||
|
||||
for i in 0..<arg.len:
|
||||
if arg[i].sym.kind in matchSet:
|
||||
copyCandidate(z, m)
|
||||
# we can shallow copy the bindings since they won't be used
|
||||
shallowCopyCandidate(z, m)
|
||||
z.callee = arg[i].typ
|
||||
if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc:
|
||||
# creating the symchoice with the type sym having typedesc type
|
||||
# breaks a lot of stuff, so we make the typedesc type here
|
||||
# mirrored from `newSymNodeTypeDesc`
|
||||
z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner)
|
||||
z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen)
|
||||
if tfUnresolved in z.callee.flags: continue
|
||||
z.calleeSym = arg[i].sym
|
||||
z.calleeScope = cmpScopes(m.c, arg[i].sym)
|
||||
@@ -2459,9 +2744,9 @@ 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, tyProxy}:
|
||||
# {tyTypeDesc, tyUntyped, tyTyped, tyError}:
|
||||
# a.typ == nil is valid
|
||||
result = a
|
||||
elif a.typ.isNil:
|
||||
@@ -2477,15 +2762,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)
|
||||
@@ -2611,8 +2898,10 @@ 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])
|
||||
n[a].typ = n[a][1].typ
|
||||
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])
|
||||
m.firstMismatch.kind = kTypeMismatch
|
||||
@@ -2635,7 +2924,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),
|
||||
@@ -2649,7 +2940,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:
|
||||
@@ -2685,7 +2978,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:
|
||||
@@ -2753,6 +3048,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
inc m.genericMatches
|
||||
inc m.exactMatches
|
||||
return
|
||||
# initCandidate may have given csNoMatch if generic params didn't match:
|
||||
if m.state == csNoMatch: return
|
||||
var marker = initIntSet()
|
||||
matchesAux(c, n, nOrig, m, marker)
|
||||
if m.state == csNoMatch: return
|
||||
@@ -2775,14 +3072,16 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
m.firstMismatch.formal = formal
|
||||
break
|
||||
else:
|
||||
# mirrored with updateDefaultParams:
|
||||
if formal.ast.kind == nkEmpty:
|
||||
# The default param value is set to empty in `instantiateProcType`
|
||||
# when the type of the default expression doesn't match the type
|
||||
# of the instantiated proc param:
|
||||
localError(c.config, m.call.info,
|
||||
("The default parameter '$1' has incompatible type " &
|
||||
"with the explicitly requested proc instantiation") %
|
||||
formal.name.s)
|
||||
pushInfoContext(c.config, m.call.info,
|
||||
if m.calleeSym != nil: m.calleeSym.detailedInfo else: "")
|
||||
typeMismatch(c.config, formal.ast.info, formal.typ, formal.ast.typ, formal.ast)
|
||||
popInfoContext(c.config)
|
||||
formal.ast.typ() = errorType(c)
|
||||
if nfDefaultRefsParam in formal.ast.flags:
|
||||
m.call.flags.incl nfDefaultRefsParam
|
||||
var defaultValue = copyTree(formal.ast)
|
||||
@@ -2791,7 +3090,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
# proc foo(x: T = 0.0)
|
||||
# foo()
|
||||
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
||||
let existing = idTableGet(m.bindings, formal.typ)
|
||||
let existing = lookup(m.bindings, formal.typ)
|
||||
if existing == nil or existing.kind == tyTypeDesc:
|
||||
# see bug #11600:
|
||||
put(m, formal.typ, defaultValue.typ)
|
||||
|
||||
@@ -385,6 +385,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
accum.maxAlign = 1
|
||||
computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
|
||||
else:
|
||||
if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and
|
||||
typ.n[0].kind == nkSym and
|
||||
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# a dummy field is generated for an object with a single field
|
||||
# with an UncheckedArray type
|
||||
assert accum.offset == 0
|
||||
accum.offset = 1
|
||||
computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
|
||||
let paddingAtEnd = int16(accum.finish())
|
||||
if typ.sym != nil and
|
||||
@@ -470,7 +477,7 @@ template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||
if size >= 0:
|
||||
let res = newIntNode(nkIntLit, size)
|
||||
res.info = node.info
|
||||
res.typ = node.typ
|
||||
res.typ() = node.typ
|
||||
res
|
||||
else:
|
||||
fallback
|
||||
@@ -484,7 +491,7 @@ template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||
if align >= 0:
|
||||
let res = newIntNode(nkIntLit, align)
|
||||
res.info = node.info
|
||||
res.typ = node.typ
|
||||
res.typ() = node.typ
|
||||
res
|
||||
else:
|
||||
fallback
|
||||
@@ -512,7 +519,7 @@ template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||
if offset >= 0:
|
||||
let tmp = newIntNode(nkIntLit, offset)
|
||||
tmp.info = node.info
|
||||
tmp.typ = node.typ
|
||||
tmp.typ() = node.typ
|
||||
tmp
|
||||
else:
|
||||
fallback
|
||||
|
||||
@@ -16,7 +16,7 @@ from trees import getMagic, getRoot
|
||||
proc callProc(a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info)
|
||||
result.add a
|
||||
result.typ = a.typ.returnType
|
||||
result.typ() = a.typ.returnType
|
||||
|
||||
# we have 4 cases to consider:
|
||||
# - a void proc --> nothing to do
|
||||
@@ -117,7 +117,7 @@ proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode =
|
||||
result = newNodeI(nkCast, fvField.info)
|
||||
result.add newNodeI(nkEmpty, fvField.info)
|
||||
result.add fvField
|
||||
result.typ = ptrType
|
||||
result.typ() = ptrType
|
||||
|
||||
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||
varSection, varInit, call, barrier, fv: PNode;
|
||||
@@ -200,7 +200,7 @@ proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode
|
||||
result = newNodeI(nkCast, argsParam.info)
|
||||
result.add newNodeI(nkEmpty, argsParam.info)
|
||||
result.add newSymNode(argsParam)
|
||||
result.typ = newType(tyPtr, idgen, objType.owner)
|
||||
result.typ() = newType(tyPtr, idgen, objType.owner)
|
||||
result.typ.rawAddSon(objType)
|
||||
|
||||
template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) =
|
||||
@@ -266,9 +266,9 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
|
||||
if argType.kind in {tyVarargs, tyOpenArray}:
|
||||
# important special case: we always create a zero-copy slice:
|
||||
let slice = newNodeI(nkCall, n.info, 4)
|
||||
slice.typ = n.typ
|
||||
slice.typ() = n.typ
|
||||
slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice))
|
||||
slice[0].typ = getSysType(g, n.info, tyInt) # fake type
|
||||
slice[0].typ() = getSysType(g, n.info, tyInt) # fake type
|
||||
var fieldB = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options)
|
||||
fieldB.typ = getSysType(g, n.info, tyInt)
|
||||
discard objType.addField(fieldB, g.cache, idgen)
|
||||
|
||||
@@ -56,7 +56,6 @@ type
|
||||
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
||||
deferDetected, tooEarly: bool
|
||||
isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields)
|
||||
inAddr: bool
|
||||
flags: TransformFlags
|
||||
graph: ModuleGraph
|
||||
idgen: IdGenerator
|
||||
@@ -100,12 +99,12 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||
let owner = getCurrOwner(c)
|
||||
result = newSymNode(r)
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PNode
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode
|
||||
|
||||
proc transformSons(c: PTransf, n: PNode): PNode =
|
||||
proc transformSons(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
result = newTransNode(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = transform(c, n[i])
|
||||
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)
|
||||
@@ -176,7 +175,7 @@ proc freshVar(c: PTransf; v: PSym): PNode =
|
||||
let owner = getCurrOwner(c)
|
||||
var newVar = copySym(v, c.idgen)
|
||||
incl(newVar.flags, sfFromGeneric)
|
||||
newVar.owner = owner
|
||||
setOwner(newVar, owner)
|
||||
result = newSymNode(newVar)
|
||||
|
||||
proc transformVarSection(c: PTransf, v: PNode): PNode =
|
||||
@@ -357,7 +356,7 @@ proc transformAsgn(c: PTransf, n: PNode): PNode =
|
||||
# given tuple type
|
||||
newTupleConstr[i] = def[0]
|
||||
|
||||
newTupleConstr.typ = rhs.typ
|
||||
newTupleConstr.typ() = rhs.typ
|
||||
|
||||
let asgnNode = newTransNode(nkAsgn, n.info, 2)
|
||||
asgnNode[0] = transform(c, n[0])
|
||||
@@ -367,6 +366,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``
|
||||
@@ -401,7 +413,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)
|
||||
@@ -409,15 +422,9 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
|
||||
result.add transform(c, v)
|
||||
|
||||
for i in 0..<c.transCon.forStmt.len - 2:
|
||||
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)
|
||||
@@ -430,7 +437,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)
|
||||
@@ -469,8 +477,8 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||
result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
|
||||
c.isIntroducingNewLocalVars = false
|
||||
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
result = transformSons(c, n)
|
||||
proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false): PNode =
|
||||
result = transformSons(c, n, noConstFold = isAddr)
|
||||
# inlining of 'var openarray' iterators; bug #19977
|
||||
if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return
|
||||
var n = result
|
||||
@@ -482,9 +490,9 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
n[0][0] = m[0]
|
||||
result = n[0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
|
||||
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
|
||||
result.typ() = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
var m = n[0][1]
|
||||
if m.kind in kinds:
|
||||
@@ -492,15 +500,25 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
|
||||
n[0][1] = m[0]
|
||||
result = n[0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
|
||||
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
|
||||
result.typ() = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
|
||||
else:
|
||||
if n[0].kind in kinds:
|
||||
if n[0].kind in kinds and
|
||||
not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
|
||||
n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
|
||||
) 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) 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
|
||||
result = n[0][0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
|
||||
proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
|
||||
## Converts 'prc' into '(thunk, nil)' so that it's compatible with
|
||||
@@ -559,7 +577,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
|
||||
else:
|
||||
result = transform(c, n[1])
|
||||
#result = transformSons(c, n)
|
||||
result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
|
||||
result.typ() = takeType(n.typ, n[1].typ, c.graph, c.idgen)
|
||||
#echo n.info, " came here and produced ", typeToString(result.typ),
|
||||
# " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
|
||||
of tyCstring:
|
||||
@@ -587,7 +605,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
|
||||
result[0] = transform(c, n[1])
|
||||
else:
|
||||
result = transform(c, n[1])
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
of tyObject:
|
||||
@@ -600,7 +618,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
|
||||
result[0] = transform(c, n[1])
|
||||
else:
|
||||
result = transform(c, n[1])
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
of tyGenericParam, tyOrdinal:
|
||||
result = transform(c, n[1])
|
||||
# happens sometimes for generated assignments, etc.
|
||||
@@ -624,6 +642,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:
|
||||
@@ -632,9 +656,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
||||
case arg.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
result = paDirectMapping
|
||||
of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
|
||||
of nkDotExpr, nkDerefExpr, nkHiddenDeref:
|
||||
result = putArgInto(arg[0], formal)
|
||||
#if result == paViaIndirection: result = paFastAsgn
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = putArgInto(arg[0], formal)
|
||||
if result == paViaIndirection: result = paFastAsgn
|
||||
of nkCurly, nkBracket:
|
||||
for i in 0..<arg.len:
|
||||
if putArgInto(arg[i], formal) != paDirectMapping:
|
||||
@@ -788,7 +814,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))
|
||||
@@ -818,7 +844,7 @@ proc transformCase(c: PTransf, n: PNode): PNode =
|
||||
# as an expr
|
||||
let kind = if n.typ != nil: nkIfExpr else: nkIfStmt
|
||||
ifs = newTransNode(kind, it.info, 0)
|
||||
ifs.typ = n.typ
|
||||
ifs.typ() = n.typ
|
||||
ifs.add(e)
|
||||
of nkElse:
|
||||
if ifs == nil: result.add(e)
|
||||
@@ -842,9 +868,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
|
||||
@@ -926,7 +961,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
|
||||
let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
|
||||
convNode[0] = newNodeI(nkEmpty, n.info)
|
||||
convNode[1] = excCall
|
||||
convNode.typ = excTypeNode.typ.toRef(c.idgen)
|
||||
convNode.typ() = excTypeNode.typ.toRef(c.idgen)
|
||||
# -> let exc = ...
|
||||
let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
|
||||
identDefs[0] = n[0][2]
|
||||
@@ -983,12 +1018,12 @@ proc transformDerefBlock(c: PTransf, n: PNode): PNode =
|
||||
# We transform (block: x)[] to (block: x[])
|
||||
let e0 = n[0]
|
||||
result = shallowCopy(e0)
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
for i in 0 ..< e0.len - 1:
|
||||
result[i] = e0[i]
|
||||
result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1])
|
||||
|
||||
proc transform(c: PTransf, n: PNode): PNode =
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
|
||||
when false:
|
||||
var oldDeferAnchor: PNode
|
||||
if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
|
||||
@@ -1054,13 +1089,8 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
of nkBreakStmt: result = transformBreak(c, n)
|
||||
of nkCallKinds:
|
||||
result = transformCall(c, n)
|
||||
of nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkHiddenDeref})
|
||||
of nkAddr:
|
||||
let oldInAddr = c.inAddr
|
||||
c.inAddr = true
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
|
||||
c.inAddr = oldInAddr
|
||||
of nkAddr, nkHiddenAddr:
|
||||
result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}, isAddr = true)
|
||||
of nkDerefExpr:
|
||||
result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
|
||||
of nkHiddenDeref:
|
||||
@@ -1140,7 +1170,7 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
|
||||
n.typ != nil and
|
||||
n.typ.kind == tyPointer
|
||||
if not exprIsPointerCast and not c.inAddr:
|
||||
if not exprIsPointerCast and not noConstFold:
|
||||
var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
|
||||
# we inline constants if they are not complex constants:
|
||||
if cnst != nil and not dontInlineConstant(n, cnst):
|
||||
@@ -1190,7 +1220,7 @@ proc liftDeferAux(n: PNode) =
|
||||
tryStmt.add deferPart
|
||||
n[i] = tryStmt
|
||||
n.sons.setLen(i+1)
|
||||
n.typ = tryStmt.typ
|
||||
n.typ() = tryStmt.typ
|
||||
goOn = true
|
||||
break
|
||||
for i in 0..n.safeLen-1:
|
||||
|
||||
@@ -147,6 +147,13 @@ proc whichPragma*(n: PNode): TSpecialWord =
|
||||
of nkCast: return wCast
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
return whichPragma(key[0])
|
||||
of nkBracketExpr:
|
||||
if n.kind notin nkPragmaCallKinds: return wInvalid
|
||||
result = whichPragma(key[0])
|
||||
if result notin {wHint, wHintAsError, wWarning, wWarningAsError}:
|
||||
# note bracket pragmas, see processNote
|
||||
result = wInvalid
|
||||
return
|
||||
else: return wInvalid
|
||||
if result in nonPragmaWordsLow..nonPragmaWordsHigh:
|
||||
result = wInvalid
|
||||
|
||||
@@ -200,7 +200,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
result = typeAllowedNode(marker, t.n, kind, c, flags)
|
||||
of tyEmpty:
|
||||
if kind in {skVar, skLet}: result = t
|
||||
of tyProxy:
|
||||
of tyError:
|
||||
# for now same as error node; we say it's a valid type as it should
|
||||
# prevent cascading errors:
|
||||
result = nil
|
||||
@@ -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
|
||||
|
||||
@@ -232,7 +232,11 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
|
||||
if result: return
|
||||
if not containsOrIncl(marker, t.id):
|
||||
case t.kind
|
||||
of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred:
|
||||
of tyGenericBody:
|
||||
# treat as atomic, containsUnresolvedType wants always false,
|
||||
# containsGenericType always gives true
|
||||
discard
|
||||
of tyGenericInst, tyAlias, tySink, tyInferred:
|
||||
result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
|
||||
else:
|
||||
for a in t.kids:
|
||||
@@ -748,6 +752,16 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
if tfThread in t.flags:
|
||||
addSep(prag)
|
||||
prag.add("gcsafe")
|
||||
var effectsOfStr = ""
|
||||
for i, a in t.paramTypes:
|
||||
let j = paramTypeToNodeIndex(i)
|
||||
if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
|
||||
addSep(effectsOfStr)
|
||||
effectsOfStr.add(t.n[j].sym.name.s)
|
||||
if effectsOfStr != "":
|
||||
addSep(prag)
|
||||
prag.add("effectsOf: ")
|
||||
prag.add(effectsOfStr)
|
||||
if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
|
||||
let effects = t.owner.typ.n[0]
|
||||
if effects.kind == nkEffectList and effects.len == effectListLen:
|
||||
@@ -776,7 +790,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
|
||||
proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
|
||||
case t.kind
|
||||
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
|
||||
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError:
|
||||
result = Zero
|
||||
of tySet, tyVar: result = firstOrd(conf, t.elementType)
|
||||
of tyArray: result = firstOrd(conf, t.indexType)
|
||||
@@ -909,7 +923,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
|
||||
result = lastOrd(conf, skipModifier(t))
|
||||
of tyUserTypeClasses:
|
||||
result = lastOrd(conf, last(t))
|
||||
of tyProxy: result = Zero
|
||||
of tyError: result = Zero
|
||||
of tyOrdinal:
|
||||
if t.hasElementType: result = lastOrd(conf, skipModifier(t))
|
||||
else:
|
||||
@@ -984,6 +998,8 @@ type
|
||||
AllowCommonBase
|
||||
PickyCAliases # be picky about the distinction between 'cint' and 'int32'
|
||||
IgnoreFlags # used for borrowed functions and methods; ignores the tfVarIsPtr flag
|
||||
PickyBackendAliases # be picky about different aliases
|
||||
IgnoreRangeShallow
|
||||
|
||||
TTypeCmpFlags* = set[TTypeCmpFlag]
|
||||
|
||||
@@ -1185,17 +1201,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
|
||||
@@ -1212,26 +1230,40 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
inc c.recCheck
|
||||
else:
|
||||
if containsOrIncl(c, a, b): return true
|
||||
template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
|
||||
if IgnoreRangeShallow in c.flags:
|
||||
x + {tyRange}
|
||||
else:
|
||||
x
|
||||
|
||||
template withoutShallowFlags(body) =
|
||||
let oldFlags = c.flags
|
||||
c.flags.excl IgnoreRangeShallow
|
||||
body
|
||||
c.flags = oldFlags
|
||||
|
||||
if x == y: return true
|
||||
var a = skipTypes(x, {tyAlias})
|
||||
let aliasSkipSet = maybeSkipRange({tyAlias})
|
||||
var a = skipTypes(x, aliasSkipSet)
|
||||
while a.kind == tyUserTypeClass and tfResolved in a.flags:
|
||||
a = skipTypes(a.last, {tyAlias})
|
||||
var b = skipTypes(y, {tyAlias})
|
||||
a = skipTypes(a.last, aliasSkipSet)
|
||||
var b = skipTypes(y, aliasSkipSet)
|
||||
while b.kind == tyUserTypeClass and tfResolved in b.flags:
|
||||
b = skipTypes(b.last, {tyAlias})
|
||||
b = skipTypes(b.last, aliasSkipSet)
|
||||
assert(a != nil)
|
||||
assert(b != nil)
|
||||
if a.kind != b.kind:
|
||||
case c.cmp
|
||||
of dcEq: return false
|
||||
of dcEqIgnoreDistinct:
|
||||
a = a.skipTypes({tyDistinct, tyGenericInst})
|
||||
b = b.skipTypes({tyDistinct, tyGenericInst})
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqOrDistinctOf:
|
||||
a = a.skipTypes({tyDistinct, tyGenericInst})
|
||||
if a.kind != b.kind: return false
|
||||
case c.cmp
|
||||
of dcEq:
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqIgnoreDistinct:
|
||||
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
|
||||
a = a.skipTypes(distinctSkipSet)
|
||||
b = b.skipTypes(distinctSkipSet)
|
||||
if a.kind != b.kind: return false
|
||||
of dcEqOrDistinctOf:
|
||||
let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
|
||||
a = a.skipTypes(distinctSkipSet)
|
||||
if a.kind != b.kind: return false
|
||||
|
||||
#[
|
||||
The following code should not run in the case either side is an generic alias,
|
||||
@@ -1239,14 +1271,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
objects ie `type A[T] = SomeObject`
|
||||
]#
|
||||
# this is required by tunique_type but makes no sense really:
|
||||
if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
|
||||
if c.cmp == dcEq and x.kind == tyGenericInst and
|
||||
IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
|
||||
let
|
||||
lhs = x.skipGenericAlias
|
||||
rhs = y.skipGenericAlias
|
||||
if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
|
||||
return false
|
||||
for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
withoutShallowFlags:
|
||||
for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
return true
|
||||
|
||||
case a.kind
|
||||
@@ -1260,6 +1294,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
let symFlagsB = if b.sym != nil: b.sym.flags else: {}
|
||||
if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
|
||||
result = symFlagsA == symFlagsB
|
||||
elif result and PickyBackendAliases in c.flags:
|
||||
let symFlagsA = if a.sym != nil: a.sym.flags else: {}
|
||||
let symFlagsB = if b.sym != nil: b.sym.flags else: {}
|
||||
if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
|
||||
result = a.id == b.id
|
||||
|
||||
of tyStatic, tyFromExpr:
|
||||
result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
|
||||
@@ -1267,15 +1306,34 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
cycleCheck()
|
||||
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
|
||||
of tyObject:
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
cycleCheck()
|
||||
result = sameObjectStructures(a, b, c) and sameFlags(a, b)
|
||||
result = sameFlags(a, b)
|
||||
if result:
|
||||
ifFastObjectTypeCheckFailed(a, b):
|
||||
cycleCheck()
|
||||
# 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:
|
||||
@@ -1284,8 +1342,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
of tyError:
|
||||
result = b.kind == tyError
|
||||
of tyTuple:
|
||||
cycleCheck()
|
||||
result = sameTuple(a, b, c) and sameFlags(a, b)
|
||||
withoutShallowFlags:
|
||||
cycleCheck()
|
||||
result = sameTuple(a, b, c) and sameFlags(a, b)
|
||||
of tyTypeDesc:
|
||||
if c.cmp == dcEqIgnoreDistinct: result = false
|
||||
elif ExactTypeDescValues in c.flags:
|
||||
@@ -1309,7 +1368,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
tyAnd, tyOr, tyNot, tyAnything, tyOwned:
|
||||
cycleCheck()
|
||||
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
|
||||
result = sameChildrenAux(a, b, c)
|
||||
withoutShallowFlags:
|
||||
result = sameChildrenAux(a, b, c)
|
||||
if result and IgnoreFlags notin c.flags:
|
||||
if IgnoreTupleFields in c.flags:
|
||||
result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
|
||||
@@ -1322,8 +1382,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
|
||||
of tyRange:
|
||||
cycleCheck()
|
||||
result = sameTypeOrNilAux(a.elementType, b.elementType, c) and
|
||||
sameValue(a.n[0], b.n[0]) and
|
||||
result = sameTypeOrNilAux(a.elementType, b.elementType, c)
|
||||
if result and IgnoreRangeShallow notin c.flags:
|
||||
result = sameValue(a.n[0], b.n[0]) and
|
||||
sameValue(a.n[1], b.n[1])
|
||||
of tyAlias, tyInferred, tyIterable:
|
||||
cycleCheck()
|
||||
@@ -1333,8 +1394,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
# The type system must distinguish between `T[int] = object #[empty]#`
|
||||
# and `T[float] = object #[empty]#`!
|
||||
cycleCheck()
|
||||
for ff, aa in underspecifiedPairs(a, b, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
withoutShallowFlags:
|
||||
for ff, aa in underspecifiedPairs(a, b, 1, -1):
|
||||
if not sameTypeAux(ff, aa, c): return false
|
||||
result = sameTypeAux(a.skipModifier, b.skipModifier, c)
|
||||
of tyNone: result = false
|
||||
of tyConcept:
|
||||
@@ -1346,6 +1408,19 @@ proc sameBackendType*(x, y: PType): bool =
|
||||
c.cmp = dcEqIgnoreDistinct
|
||||
result = sameTypeAux(x, y, c)
|
||||
|
||||
proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
|
||||
var c = initSameTypeClosure()
|
||||
c.flags.incl IgnoreTupleFields
|
||||
c.flags.incl IgnoreRangeShallow
|
||||
c.cmp = dcEqIgnoreDistinct
|
||||
result = sameTypeAux(x, y, c)
|
||||
|
||||
proc sameBackendTypePickyAliases*(x, y: PType): bool =
|
||||
var c = initSameTypeClosure()
|
||||
c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
|
||||
c.cmp = dcEqIgnoreDistinct
|
||||
result = sameTypeAux(x, y, c)
|
||||
|
||||
proc compareTypes*(x, y: PType,
|
||||
cmp: TDistinctCompare = dcEq,
|
||||
flags: TTypeCmpFlags = {}): bool =
|
||||
@@ -1406,6 +1481,8 @@ proc commonSuperclass*(a, b: PType): PType =
|
||||
return t
|
||||
y = y.baseClass
|
||||
|
||||
proc lacksMTypeField*(typ: PType): bool {.inline.} =
|
||||
(typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
|
||||
|
||||
include sizealignoffsetimpl
|
||||
|
||||
@@ -1442,6 +1519,23 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
|
||||
proc containsGenericType*(t: PType): bool =
|
||||
result = iterOverType(t, containsGenericTypeIter, nil)
|
||||
|
||||
proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
|
||||
if tfUnresolved in t.flags: return true
|
||||
case t.kind
|
||||
of tyStatic:
|
||||
return t.n == nil
|
||||
of tyTypeDesc:
|
||||
if t.base.kind == tyNone: return true
|
||||
if containsUnresolvedTypeIter(t.base, closure): return true
|
||||
return false
|
||||
of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
proc containsUnresolvedType*(t: PType): bool =
|
||||
result = iterOverType(t, containsUnresolvedTypeIter, nil)
|
||||
|
||||
proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
|
||||
if t.kind == tyDistinct:
|
||||
result = t.elementType
|
||||
@@ -1611,7 +1705,7 @@ proc skipHidden*(n: PNode): PNode =
|
||||
|
||||
proc skipConvTakeType*(n: PNode): PNode =
|
||||
result = n.skipConv
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
|
||||
proc isEmptyContainer*(t: PType): bool =
|
||||
case t.kind
|
||||
@@ -1651,7 +1745,7 @@ proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode =
|
||||
result = n
|
||||
else:
|
||||
result = copyTree(result)
|
||||
result.typ = dest
|
||||
result.typ() = dest
|
||||
else:
|
||||
result = n
|
||||
|
||||
@@ -1725,6 +1819,13 @@ proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, co
|
||||
of efTagsIllegal:
|
||||
msg.add "\n.notTag catched an illegal effect"
|
||||
|
||||
proc typeNameAndDesc*(t: PType): string =
|
||||
result = typeToString(t)
|
||||
let desc = typeToString(t, preferDesc)
|
||||
if result != desc:
|
||||
result.add(" = ")
|
||||
result.add(desc)
|
||||
|
||||
proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
|
||||
if formal.kind != tyError and actual.kind != tyError:
|
||||
let actualStr = typeToString(actual)
|
||||
@@ -1855,5 +1956,85 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc lacksMTypeField*(typ: PType): bool {.inline.} =
|
||||
(typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
|
||||
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
|
||||
|
||||
@@ -441,11 +441,15 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
|
||||
|
||||
of nkIfStmt, nkIfExpr:
|
||||
for i in 0..<n.len:
|
||||
inc c.inConditional
|
||||
destMightOwn(c, dest, n[i].lastSon)
|
||||
dec c.inConditional
|
||||
|
||||
of nkCaseStmt:
|
||||
for i in 1..<n.len:
|
||||
inc c.inConditional
|
||||
destMightOwn(c, dest, n[i].lastSon)
|
||||
dec c.inConditional
|
||||
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
if n.len > 0:
|
||||
@@ -498,8 +502,17 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
|
||||
# we know the result is derived from the first argument:
|
||||
var roots: seq[(PSym, int)] = @[]
|
||||
allRoots(n[1], roots, RootEscapes)
|
||||
for r in roots:
|
||||
connect(c, dest.sym, r[0], n[1].info)
|
||||
if roots.len == 0 and c.inConditional > 0:
|
||||
# when in a conditional expression,
|
||||
# to ensure that the first argument isn't outlived
|
||||
# by the lvalue, we need find the root, otherwise
|
||||
# it is probably a local temporary
|
||||
# (e.g. a return value from a call),
|
||||
# we should prevent cursorfication
|
||||
dest.flags.incl preventCursor
|
||||
else:
|
||||
for r in roots:
|
||||
connect(c, dest.sym, r[0], n[1].info)
|
||||
|
||||
else:
|
||||
let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone
|
||||
|
||||
128
compiler/vm.nim
128
compiler/vm.nim
@@ -202,7 +202,7 @@ proc copyValue(src: PNode): PNode =
|
||||
return src
|
||||
result = newNode(src.kind)
|
||||
result.info = src.info
|
||||
result.typ = src.typ
|
||||
result.typ() = src.typ
|
||||
result.flags = src.flags * PersistentNodeFlags
|
||||
result.comment = src.comment
|
||||
when defined(useNodeIds):
|
||||
@@ -507,7 +507,7 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
|
||||
setLen(node.sons, newLen)
|
||||
if oldLen < newLen:
|
||||
for i in oldLen..<newLen:
|
||||
node[i] = getNullValue(typ.elementType, info, c.config)
|
||||
node[i] = getNullValue(c, typ.elementType, info, c.config)
|
||||
|
||||
const
|
||||
errNilAccess = "attempt to access a nil address"
|
||||
@@ -609,7 +609,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcYldVal: assert false
|
||||
of opcAsgnInt:
|
||||
decodeB(rkInt)
|
||||
regs[ra].intVal = regs[rb].intVal
|
||||
if regs[rb].kind == rkInt:
|
||||
regs[ra].intVal = regs[rb].intVal
|
||||
else:
|
||||
stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind)
|
||||
of opcAsgnFloat:
|
||||
decodeB(rkFloat)
|
||||
regs[ra].floatVal = regs[rb].floatVal
|
||||
@@ -676,16 +679,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
assert regs[rb].kind == rkNode
|
||||
let nb = regs[rb].node
|
||||
case nb.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
ensureKind(rkInt)
|
||||
regs[ra].intVal = nb.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
ensureKind(rkFloat)
|
||||
regs[ra].floatVal = nb.floatVal
|
||||
if nb == nil:
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
else:
|
||||
ensureKind(rkNode)
|
||||
regs[ra].node = nb
|
||||
case nb.kind
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
ensureKind(rkInt)
|
||||
regs[ra].intVal = nb.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
ensureKind(rkFloat)
|
||||
regs[ra].floatVal = nb.floatVal
|
||||
else:
|
||||
ensureKind(rkNode)
|
||||
regs[ra].node = nb
|
||||
of opcSlice:
|
||||
# A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
|
||||
# nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
|
||||
@@ -850,25 +856,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcLdObj:
|
||||
# a = b.c
|
||||
decodeBC(rkNode)
|
||||
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
|
||||
case src.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
# for nkPtrLit, this could be supported in the future, use something like:
|
||||
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
|
||||
# where we compute the offset in bytes for field rc
|
||||
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
|
||||
of nkObjConstr:
|
||||
let n = src[rc + 1].skipColon
|
||||
regs[ra].node = n
|
||||
of nkTupleConstr:
|
||||
let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
|
||||
src[rc]
|
||||
else:
|
||||
src[rc].skipColon
|
||||
regs[ra].node = n
|
||||
if rb >= regs.len or regs[rb].kind == rkNone or
|
||||
(regs[rb].kind == rkNode and regs[rb].node == nil) or
|
||||
(regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil):
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
else:
|
||||
let n = src[rc]
|
||||
regs[ra].node = n
|
||||
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
|
||||
case src.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
# for nkPtrLit, this could be supported in the future, use something like:
|
||||
# derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
|
||||
# where we compute the offset in bytes for field rc
|
||||
stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
|
||||
of nkObjConstr:
|
||||
let n = src[rc + 1].skipColon
|
||||
regs[ra].node = n
|
||||
of nkTupleConstr:
|
||||
let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
|
||||
src[rc]
|
||||
else:
|
||||
src[rc].skipColon
|
||||
regs[ra].node = n
|
||||
else:
|
||||
let n = src[rc]
|
||||
regs[ra].node = n
|
||||
of opcLdObjAddr:
|
||||
# a = addr(b.c)
|
||||
decodeBC(rkNodeAddr)
|
||||
@@ -894,8 +905,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
stackTrace(c, tos, pc, errNilAccess)
|
||||
elif dest[shiftedRb].kind == nkExprColonExpr:
|
||||
writeField(dest[shiftedRb][1], regs[rc])
|
||||
dest[shiftedRb][1].flags.incl nfSkipFieldChecking
|
||||
else:
|
||||
writeField(dest[shiftedRb], regs[rc])
|
||||
dest[shiftedRb].flags.incl nfSkipFieldChecking
|
||||
of opcWrStrIdx:
|
||||
decodeBC(rkNode)
|
||||
let idx = regs[rb].intVal.int
|
||||
@@ -1263,6 +1276,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
createSet(regs[ra])
|
||||
move(regs[ra].node.sons,
|
||||
nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons)
|
||||
of opcXorSet:
|
||||
decodeBC(rkNode)
|
||||
createSet(regs[ra])
|
||||
move(regs[ra].node.sons,
|
||||
nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons)
|
||||
of opcConcatStr:
|
||||
decodeBC(rkNode)
|
||||
createStr regs[ra]
|
||||
@@ -1363,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
|
||||
else:
|
||||
regs[ra].intVal = parseBiggestFloat(regs[ra].node.strVal, rcAddr.floatVal)
|
||||
regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
|
||||
|
||||
of opcRangeChck:
|
||||
let rb = instr.regB
|
||||
@@ -1378,7 +1396,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let rb = instr.regB
|
||||
let rc = instr.regC
|
||||
let bb = regs[rb].node
|
||||
if bb.kind == nkNilLit:
|
||||
stackTrace(c, tos, pc, "attempt to call nil closure")
|
||||
let isClosure = bb.kind == nkTupleConstr
|
||||
if isClosure and bb[0].kind == nkNilLit:
|
||||
stackTrace(c, tos, pc, "attempt to call nil closure")
|
||||
let prc = if not isClosure: bb.sym else: bb[0].sym
|
||||
if prc.offset < -1:
|
||||
# it's a callback:
|
||||
@@ -1418,7 +1440,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
|
||||
newSeq(newFrame.slots, prc.offset+ord(isClosure))
|
||||
if not isEmptyType(prc.typ.returnType):
|
||||
putIntoReg(newFrame.slots[0], getNullValue(prc.typ.returnType, prc.info, c.config))
|
||||
putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config))
|
||||
for i in 1..rc-1:
|
||||
newFrame.slots[i] = regs[rb+i]
|
||||
if isClosure:
|
||||
@@ -1515,10 +1537,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
# Set the `name` field of the exception
|
||||
var exceptionNameNode = newStrNode(nkStrLit, c.currentExceptionA.typ.sym.name.s)
|
||||
if c.currentExceptionA[2].kind == nkExprColonExpr:
|
||||
exceptionNameNode.typ = c.currentExceptionA[2][1].typ
|
||||
exceptionNameNode.typ() = c.currentExceptionA[2][1].typ
|
||||
c.currentExceptionA[2][1] = exceptionNameNode
|
||||
else:
|
||||
exceptionNameNode.typ = c.currentExceptionA[2].typ
|
||||
exceptionNameNode.typ() = c.currentExceptionA[2].typ
|
||||
c.currentExceptionA[2] = exceptionNameNode
|
||||
c.exceptionInstr = pc
|
||||
|
||||
@@ -1551,7 +1573,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcNew:
|
||||
ensureKind(rkNode)
|
||||
let typ = c.types[instr.regBx - wordExcess]
|
||||
regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
|
||||
regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
|
||||
regs[ra].node.flags.incl nfIsRef
|
||||
of opcNewSeq:
|
||||
let typ = c.types[instr.regBx - wordExcess]
|
||||
@@ -1560,10 +1582,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let instr2 = c.code[pc]
|
||||
let count = regs[instr2.regA].intVal.int
|
||||
regs[ra].node = newNodeI(nkBracket, c.debug[pc])
|
||||
regs[ra].node.typ = typ
|
||||
regs[ra].node.typ() = typ
|
||||
newSeq(regs[ra].node.sons, count)
|
||||
for i in 0..<count:
|
||||
regs[ra].node[i] = getNullValue(typ.elementType, c.debug[pc], c.config)
|
||||
regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config)
|
||||
of opcNewStr:
|
||||
decodeB(rkNode)
|
||||
regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
|
||||
@@ -1575,7 +1597,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcLdNull:
|
||||
ensureKind(rkNode)
|
||||
let typ = c.types[instr.regBx - wordExcess]
|
||||
regs[ra].node = getNullValue(typ, c.debug[pc], c.config)
|
||||
regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
|
||||
# opcLdNull really is the gist of the VM's problems: should it load
|
||||
# a fresh null to regs[ra].node or to regs[ra].node[]? This really
|
||||
# depends on whether regs[ra] represents the variable itself or whether
|
||||
@@ -1975,7 +1997,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
regs[ra].node.info = n.info
|
||||
regs[ra].node.typ = n.typ
|
||||
regs[ra].node.typ() = n.typ
|
||||
of opcNCopyLineInfo:
|
||||
decodeB(rkNode)
|
||||
regs[ra].node.info = regs[rb].node.info
|
||||
@@ -2055,7 +2077,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
ensureKind(rkNode)
|
||||
regs[ra].node = temp
|
||||
regs[ra].node.info = c.debug[pc]
|
||||
regs[ra].node.typ = typ
|
||||
regs[ra].node.typ() = typ
|
||||
of opcConv:
|
||||
let rb = instr.regB
|
||||
inc pc
|
||||
@@ -2304,7 +2326,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
|
||||
# setup parameters:
|
||||
if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
|
||||
putIntoReg(tos.slots[0], getNullValue(sym.typ.returnType, sym.info, c.config))
|
||||
putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, sym.info, c.config))
|
||||
# XXX We could perform some type checking here.
|
||||
for i in 0..<sym.typ.paramsLen:
|
||||
putIntoReg(tos.slots[i+1], args[i])
|
||||
@@ -2315,9 +2337,17 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
localError(c.config, sym.info,
|
||||
"NimScript: attempt to call non-routine: " & sym.name.s)
|
||||
|
||||
proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
result.typ() = newType(tyError, idgen, owner)
|
||||
result.typ.flags.incl tfCheckedForDestructor
|
||||
|
||||
proc evalStmt*(c: PCtx, n: PNode) =
|
||||
let n = transformExpr(c.graph, c.idgen, c.module, n)
|
||||
let start = genStmt(c, n)
|
||||
if c.cannotEval:
|
||||
c.cannotEval = false
|
||||
return
|
||||
# execute new instructions; this redundant opcEof check saves us lots
|
||||
# of allocations in 'execute':
|
||||
if c.code[start].opcode != opcEof:
|
||||
@@ -2328,7 +2358,10 @@ proc evalExpr*(c: PCtx, n: PNode): PNode =
|
||||
# `nim --eval:"expr"` might've used it at some point for idetools; could
|
||||
# be revived for nimsuggest
|
||||
let n = transformExpr(c.graph, c.idgen, c.module, n)
|
||||
c.cannotEval = false
|
||||
let start = genExpr(c, n)
|
||||
if c.cannotEval:
|
||||
return errorNode(c.idgen, c.module, n)
|
||||
assert c.code[start].opcode != opcEof
|
||||
result = execute(c, start)
|
||||
|
||||
@@ -2381,7 +2414,10 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator;
|
||||
var c = PCtx g.vm
|
||||
let oldMode = c.mode
|
||||
c.mode = mode
|
||||
c.cannotEval = false
|
||||
let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
|
||||
if c.cannotEval:
|
||||
return errorNode(idgen, prc, n)
|
||||
if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
|
||||
assert c.code[start].opcode != opcEof
|
||||
when debugEchoCode: c.echoCode start
|
||||
@@ -2438,7 +2474,7 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
|
||||
var n = x
|
||||
if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n[1]
|
||||
n.flags.incl nfIsRef
|
||||
n.typ = x.typ
|
||||
n.typ() = x.typ
|
||||
result = TFullReg(kind: rkNode, node: n)
|
||||
|
||||
iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
|
||||
@@ -2452,11 +2488,6 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
|
||||
# to prevent endless recursion in macro instantiation
|
||||
const evalMacroLimit = 1000
|
||||
|
||||
#proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
|
||||
# result = newNodeI(nkEmpty, n.info)
|
||||
# result.typ = newType(tyError, idgen, owner)
|
||||
# result.typ.flags.incl tfCheckedForDestructor
|
||||
|
||||
proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int;
|
||||
n, nOrig: PNode, sym: PSym): PNode =
|
||||
#if g.config.errorCounter > 0: return errorNode(idgen, module, n)
|
||||
@@ -2480,7 +2511,10 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
|
||||
c.comesFromHeuristic.line = 0'u16
|
||||
c.callsite = nOrig
|
||||
c.templInstCounter = templInstCounter
|
||||
c.cannotEval = false
|
||||
let start = genProc(c, sym)
|
||||
if c.cannotEval:
|
||||
return errorNode(idgen, module, n)
|
||||
|
||||
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
|
||||
let maxSlots = sym.offset
|
||||
|
||||
@@ -100,7 +100,7 @@ type
|
||||
opcEqRef, opcEqNimNode, opcSameNodeType,
|
||||
opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
|
||||
opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
|
||||
opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr,
|
||||
opcMulSet, opcPlusSet, opcMinusSet, opcXorSet, opcConcatStr,
|
||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||
opcIsNil, opcOf, opcIs,
|
||||
opcParseFloat, opcConv, opcCast,
|
||||
@@ -270,6 +270,7 @@ type
|
||||
templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst
|
||||
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
|
||||
procToCodePos*: Table[int, int]
|
||||
cannotEval*: bool
|
||||
|
||||
PStackFrame* = ref TStackFrame
|
||||
TStackFrame* {.acyclic.} = object
|
||||
|
||||
@@ -35,7 +35,7 @@ proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLi
|
||||
sym.magic = m
|
||||
sym.typ = t
|
||||
result = newSymNode(sym)
|
||||
result.typ = t
|
||||
result.typ() = t
|
||||
|
||||
proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
|
||||
result = newSymNode(s)
|
||||
@@ -52,7 +52,7 @@ proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; inf
|
||||
for a in t.kids:
|
||||
if a == nil:
|
||||
let voidt = atomicTypeX(cache, "void", mVoid, t, info, idgen)
|
||||
voidt.typ = newType(tyVoid, idgen, t.owner)
|
||||
voidt.typ() = newType(tyVoid, idgen, t.owner)
|
||||
result.add voidt
|
||||
else:
|
||||
result.add mapTypeToAstX(cache, a, info, idgen, inst)
|
||||
@@ -136,7 +136,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
if allowRecursion:
|
||||
result = mapTypeToAstR(t.skipModifier, info)
|
||||
# keep original type info for getType calls on the output node:
|
||||
result.typ = t
|
||||
result.typ() = t
|
||||
else:
|
||||
result = newNodeX(nkBracketExpr)
|
||||
#result.add mapTypeToAst(t.last, info)
|
||||
@@ -146,7 +146,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
else:
|
||||
result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
|
||||
# keep original type info for getType calls on the output node:
|
||||
result.typ = t
|
||||
result.typ() = t
|
||||
of tyGenericBody:
|
||||
if inst:
|
||||
result = mapTypeToAstR(t.typeBodyImpl, info)
|
||||
@@ -237,7 +237,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
|
||||
of tyProc:
|
||||
if inst:
|
||||
result = newNodeX(nkProcTy)
|
||||
result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy)
|
||||
var fp = newNodeX(nkFormalParams)
|
||||
if t.returnType == nil:
|
||||
fp.add newNodeI(nkEmpty, info)
|
||||
@@ -246,8 +246,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
for i in FirstParamAt..<t.kidsLen:
|
||||
fp.add newIdentDefs(t.n[i], t[i])
|
||||
result.add fp
|
||||
result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
|
||||
else: newNodeI(nkEmpty, info)
|
||||
var prag =
|
||||
if t.n[0].len > 0:
|
||||
t.n[0][pragmasEffects].copyTree
|
||||
else:
|
||||
newNodeI(nkEmpty, info)
|
||||
if t.callConv != ccClosure or tfExplicitCallConv in t.flags:
|
||||
if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info)
|
||||
prag.add newIdentNode(getIdent(cache, $t.callConv), info)
|
||||
result.add prag
|
||||
else:
|
||||
result = mapTypeToBracket("proc", mNone, t, info)
|
||||
of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
|
||||
@@ -282,7 +289,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
of tyUInt32: result = atomicType("uint32", mUInt32)
|
||||
of tyUInt64: result = atomicType("uint64", mUInt64)
|
||||
of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info)
|
||||
of tyProxy: result = atomicType("error", mNone)
|
||||
of tyError: result = atomicType("error", mNone)
|
||||
of tyBuiltInTypeClass:
|
||||
result = mapTypeToBracket("builtinTypeClass", mNone, t, info)
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
|
||||
@@ -53,6 +53,7 @@ type
|
||||
gfNode # Affects how variables are loaded - always loads as rkNode
|
||||
gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
|
||||
gfIsParam # do not deepcopy parameters, they are immutable
|
||||
gfIsSinkParam # deepcopy sink parameters
|
||||
TGenFlags = set[TGenFlag]
|
||||
|
||||
proc debugInfo(c: PCtx; info: TLineInfo): string =
|
||||
@@ -245,7 +246,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister =
|
||||
|
||||
proc freeTemp(c: PCtx; r: TRegister) =
|
||||
let c = c.prc
|
||||
if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
|
||||
if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
|
||||
# this seems to cause https://github.com/nim-lang/Nim/issues/10647
|
||||
c.regInfo[r].inUse = false
|
||||
|
||||
@@ -357,12 +358,13 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||
#if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}:
|
||||
if i != dest:
|
||||
when not defined(release):
|
||||
if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
|
||||
slotTempInt,
|
||||
slotTempFloat,
|
||||
slotTempStr,
|
||||
slotTempComplex}:
|
||||
raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
|
||||
if c.config.cmd != cmdCheck:
|
||||
if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
|
||||
slotTempInt,
|
||||
slotTempFloat,
|
||||
slotTempStr,
|
||||
slotTempComplex}:
|
||||
raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
|
||||
c.prc.regInfo[i] = (inUse: false, kind: slotEmpty)
|
||||
|
||||
c.clearDest(n, dest)
|
||||
@@ -619,10 +621,17 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let fntyp = skipTypes(n[0].typ, abstractInst)
|
||||
for i in 0..<n.len:
|
||||
var r: TRegister = x+i
|
||||
c.gen(n[i], r, {gfIsParam})
|
||||
if i >= fntyp.signatureLen:
|
||||
c.gen(n[i], r, {gfIsParam})
|
||||
internalAssert c.config, tfVarargs in fntyp.flags
|
||||
c.gABx(n, opcSetType, r, c.genType(n[i].typ))
|
||||
else:
|
||||
if fntyp[i] != nil and fntyp[i].kind == tySink and
|
||||
fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}:
|
||||
c.gen(n[i], r, {gfIsSinkParam})
|
||||
else:
|
||||
c.gen(n[i], r, {gfIsParam})
|
||||
|
||||
if dest < 0:
|
||||
c.gABC(n, opcIndCall, 0, x, n.len)
|
||||
else:
|
||||
@@ -696,6 +705,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||
let dest = c.genx(le, {gfNodeAddr})
|
||||
c.gABC(le, opcWrDeref, dest, 0, value)
|
||||
c.freeTemp(dest)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if sameBackendType(le.typ, le[1].typ):
|
||||
genAsgnPatch(c, le[1], value)
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -868,7 +880,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
genBinaryABC(c, n, dest, opc)
|
||||
c.genNarrow(n, dest)
|
||||
|
||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) =
|
||||
let t2 = n.typ.skipTypes({tyDistinct})
|
||||
let targ2 = arg.typ.skipTypes({tyDistinct})
|
||||
|
||||
@@ -882,7 +894,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||
result = false
|
||||
|
||||
if implicitConv():
|
||||
gen(c, arg, dest)
|
||||
gen(c, arg, dest, flags)
|
||||
return
|
||||
|
||||
let tmp = c.genx(arg)
|
||||
@@ -900,9 +912,15 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
template isSigned(typ: PType): bool {.dirty.} =
|
||||
typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or
|
||||
typ.kind in {tyInt..tyInt64}
|
||||
template isUnsigned(typ: PType): bool {.dirty.} =
|
||||
typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or
|
||||
typ.kind in {tyUInt..tyUInt64, tyChar, tyBool}
|
||||
|
||||
const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
|
||||
var signedIntegers = {tyInt..tyInt64}
|
||||
var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
|
||||
|
||||
let src = n[1].typ.skipTypes(abstractRange)#.kind
|
||||
let dst = n[0].typ.skipTypes(abstractRange)#.kind
|
||||
let srcSize = getSize(c.config, src)
|
||||
@@ -914,12 +932,12 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
c.gABC(n, opcAsgnInt, dest, tmp)
|
||||
if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types
|
||||
if dst.kind in signedIntegers: # we need to do sign extensions
|
||||
if isSigned(dst): # we need to do sign extensions
|
||||
if dstSize <= srcSize:
|
||||
# Sign extension can be omitted when the size increases.
|
||||
c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8))
|
||||
elif dst.kind in unsignedIntegers:
|
||||
if src.kind in signedIntegers or dstSize < srcSize:
|
||||
elif isUnsigned(dst):
|
||||
if isSigned(src) or dstSize < srcSize:
|
||||
# Cast from signed to unsigned always needs narrowing. Cast
|
||||
# from unsigned to unsigned only needs narrowing when target
|
||||
# is smaller than source.
|
||||
@@ -947,7 +965,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
if src.kind == tyFloat32:
|
||||
c.gABC(n, opcCastFloatToInt32, dest, tmp)
|
||||
if dst.kind in unsignedIntegers:
|
||||
if isUnsigned(dst):
|
||||
# integers are sign extended by default.
|
||||
# since there is no opcCastFloatToUInt32, narrowing should do the trick.
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(32))
|
||||
@@ -1044,7 +1062,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
|
||||
else:
|
||||
(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||
of mOr: c.genAndOr(n, opcTJmp, dest)
|
||||
@@ -1183,7 +1201,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
|
||||
genConv(c, n, n[1], dest)
|
||||
genConv(c, n, n[1], dest, flags)
|
||||
of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
|
||||
of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
|
||||
of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
|
||||
@@ -1194,6 +1212,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
of mMulSet: genBinarySet(c, n, dest, opcMulSet)
|
||||
of mPlusSet: genBinarySet(c, n, dest, opcPlusSet)
|
||||
of mMinusSet: genBinarySet(c, n, dest, opcMinusSet)
|
||||
of mXorSet: genBinarySet(c, n, dest, opcXorSet)
|
||||
of mConStrStr: genVarargsABC(c, n, dest, opcConcatStr)
|
||||
of mInSet: genBinarySet(c, n, dest, opcContainsSet)
|
||||
of mRepr: genUnaryABC(c, n, dest, opcRepr)
|
||||
@@ -1455,9 +1474,9 @@ proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
|
||||
result = copyNode(n[0])
|
||||
result.add m[0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
|
||||
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
|
||||
result.typ() = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
var m = n[0][1]
|
||||
if m.kind in {nkDerefExpr, nkHiddenDeref}:
|
||||
@@ -1466,9 +1485,9 @@ proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
|
||||
result.add n[0][0]
|
||||
result.add m[0]
|
||||
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
result.typ = n.typ
|
||||
result.typ() = n.typ
|
||||
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
|
||||
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
|
||||
result.typ() = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
|
||||
else:
|
||||
if n[0].kind in {nkDerefExpr, nkHiddenDeref}:
|
||||
# addr ( deref ( x )) --> x
|
||||
@@ -1523,7 +1542,14 @@ proc setSlot(c: PCtx; v: PSym) =
|
||||
if v.position == 0:
|
||||
v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
|
||||
|
||||
proc cannotEval(c: PCtx; n: PNode) {.noinline.} =
|
||||
template cannotEval(c: PCtx; n: PNode) =
|
||||
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
|
||||
return
|
||||
globalError(c.config, n.info, "cannot evaluate at compile time: " &
|
||||
n.renderTree)
|
||||
|
||||
@@ -1646,6 +1672,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
c.freeTemp(cc)
|
||||
else:
|
||||
gen(c, ri, dest)
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
if sameBackendType(le.typ, le[1].typ):
|
||||
genAsgn(c, le[1], ri, requiresCopy)
|
||||
else:
|
||||
let dest = c.genx(le, {gfNodeAddr})
|
||||
genAsgn(c, dest, ri, requiresCopy)
|
||||
@@ -1653,7 +1682,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
|
||||
proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
|
||||
var n = newNode(nkType)
|
||||
n.typ = t
|
||||
n.typ() = t
|
||||
genLit(c, n, dest)
|
||||
|
||||
proc isEmptyBody(n: PNode): bool =
|
||||
@@ -1684,10 +1713,10 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
|
||||
localError(c.config, info,
|
||||
"cannot 'importc' variable at compile time; " & s.name.s)
|
||||
|
||||
proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode
|
||||
proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode
|
||||
|
||||
proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
|
||||
c.globals.add(getNullValue(s.typ, n.info, c.config))
|
||||
c.globals.add(getNullValue(c, s.typ, n.info, c.config))
|
||||
s.position = c.globals.len
|
||||
# This is rather hard to support, due to the laziness of the VM code
|
||||
# generator. See tests/compile/tmacro2 for why this is necessary:
|
||||
@@ -1723,6 +1752,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
c.gABx(n, opcLdGlobalAddr, dest, s.position)
|
||||
elif isImportcVar:
|
||||
c.gABx(n, opcLdGlobalDerefFFI, dest, s.position)
|
||||
elif gfIsSinkParam in flags:
|
||||
genAsgn(c, dest, n, requiresCopy = true)
|
||||
elif fitsRegister(s.typ) and gfNode notin flags:
|
||||
var cc = c.getTemp(n.typ)
|
||||
c.gABx(n, opcLdGlobal, cc, s.position)
|
||||
@@ -1736,7 +1767,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
s.kind in {skParam, skResult}):
|
||||
if dest < 0:
|
||||
dest = s.position + ord(s.kind == skParam)
|
||||
internalAssert(c.config, c.prc.regInfo[dest].kind < slotSomeTemp)
|
||||
internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp)
|
||||
else:
|
||||
# we need to generate an assignment:
|
||||
let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and
|
||||
@@ -1820,7 +1851,7 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
|
||||
let fieldName = $accessExpr[1]
|
||||
let msg = genFieldDefect(c.config, fieldName, disc.sym)
|
||||
let strLit = newStrNode(msg, accessExpr[1].info)
|
||||
strLit.typ = strType
|
||||
strLit.typ() = strType
|
||||
c.genLit(strLit, msgReg)
|
||||
c.gABC(n, opcInvalidField, msgReg, discVal)
|
||||
c.freeTemp(discVal)
|
||||
@@ -1866,21 +1897,21 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr
|
||||
genArrAccessOpcode(c, n, dest, opc, flags)
|
||||
|
||||
proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
|
||||
proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
|
||||
if t != nil and t.baseClass != nil:
|
||||
let b = skipTypes(t.baseClass, skipPtrs)
|
||||
getNullValueAux(b, b.n, result, conf, currPosition)
|
||||
getNullValueAux(c, b, b.n, result, conf, currPosition)
|
||||
case obj.kind
|
||||
of nkRecList:
|
||||
for i in 0..<obj.len: getNullValueAux(nil, obj[i], result, conf, currPosition)
|
||||
for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition)
|
||||
of nkRecCase:
|
||||
getNullValueAux(nil, obj[0], result, conf, currPosition)
|
||||
getNullValueAux(c, nil, obj[0], result, conf, currPosition)
|
||||
for i in 1..<obj.len:
|
||||
getNullValueAux(nil, lastSon(obj[i]), result, conf, currPosition)
|
||||
getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition)
|
||||
of nkSym:
|
||||
let field = newNodeI(nkExprColonExpr, result.info)
|
||||
field.add(obj)
|
||||
let value = getNullValue(obj.sym.typ, result.info, conf)
|
||||
let value = getNullValue(c, obj.sym.typ, result.info, conf)
|
||||
value.flags.incl nfSkipFieldChecking
|
||||
field.add(value)
|
||||
result.add field
|
||||
@@ -1888,7 +1919,7 @@ proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currP
|
||||
inc currPosition
|
||||
else: globalError(conf, result.info, "cannot create null element for: " & $obj)
|
||||
|
||||
proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
|
||||
proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
|
||||
var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyBool, tyEnum, tyChar, tyInt..tyInt64:
|
||||
@@ -1908,22 +1939,22 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
|
||||
result = newNodeIT(nkNilLit, info, t)
|
||||
else:
|
||||
result = newNodeIT(nkTupleConstr, info, t)
|
||||
result.add(newNodeIT(nkNilLit, info, t))
|
||||
result.add(newNodeIT(nkNilLit, info, t))
|
||||
result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
|
||||
result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
|
||||
of tyObject:
|
||||
result = newNodeIT(nkObjConstr, info, t)
|
||||
result.add(newNodeIT(nkEmpty, info, t))
|
||||
# initialize inherited fields, and all in the correct order:
|
||||
var currPosition = 0
|
||||
getNullValueAux(t, t.n, result, conf, currPosition)
|
||||
getNullValueAux(c, t, t.n, result, conf, currPosition)
|
||||
of tyArray:
|
||||
result = newNodeIT(nkBracket, info, t)
|
||||
for i in 0..<toInt(lengthOrd(conf, t)):
|
||||
result.add getNullValue(elemType(t), info, conf)
|
||||
result.add getNullValue(c, elemType(t), info, conf)
|
||||
of tyTuple:
|
||||
result = newNodeIT(nkTupleConstr, info, t)
|
||||
for a in t.kids:
|
||||
result.add getNullValue(a, info, conf)
|
||||
result.add getNullValue(c, a, info, conf)
|
||||
of tySet:
|
||||
result = newNodeIT(nkCurly, info, t)
|
||||
of tySequence, tyOpenArray:
|
||||
@@ -1951,7 +1982,7 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||
if s.position == 0:
|
||||
if importcCond(c, s): c.importcSym(a.info, s)
|
||||
else:
|
||||
let sa = getNullValue(s.typ, a.info, c.config)
|
||||
let sa = getNullValue(c, s.typ, a.info, c.config)
|
||||
#if s.ast.isNil: getNullValue(s.typ, a.info)
|
||||
#else: s.ast
|
||||
assert sa.kind != nkCall
|
||||
@@ -1973,7 +2004,7 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||
# the problem is that closure types are tuples in VM, but the types of its children
|
||||
# shouldn't have the same type as closure types.
|
||||
let tmp = c.genx(a[0], {gfNodeAddr})
|
||||
let sa = getNullValue(s.typ, a.info, c.config)
|
||||
let sa = getNullValue(c, s.typ, a.info, c.config)
|
||||
let val = c.genx(sa)
|
||||
c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val)
|
||||
c.freeTemp(val)
|
||||
@@ -2158,7 +2189,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
if n[0].kind == nkSym:
|
||||
let s = n[0].sym
|
||||
if s.magic != mNone:
|
||||
genMagic(c, n, dest, s.magic)
|
||||
genMagic(c, n, dest, flags, s.magic)
|
||||
elif s.kind == skMethod:
|
||||
localError(c.config, n.info, "cannot call method " & s.name.s &
|
||||
" at compile time")
|
||||
@@ -2176,7 +2207,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
genLit(c, n, dest)
|
||||
of nkUIntLit..pred(nkNilLit): genLit(c, n, dest)
|
||||
of nkNilLit:
|
||||
if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest)
|
||||
if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest)
|
||||
else: unused(c, n, dest)
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
unused(c, n, dest)
|
||||
@@ -2215,11 +2246,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
unused(c, n, dest)
|
||||
gen(c, n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
genConv(c, n, n[1], dest)
|
||||
genConv(c, n, n[1], dest, flags)
|
||||
of nkObjDownConv:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
of nkObjUpConv:
|
||||
genConv(c, n, n[0], dest)
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
of nkVarSection, nkLetSection:
|
||||
unused(c, n, dest)
|
||||
genVarSection(c, n)
|
||||
@@ -2228,18 +2259,21 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
#discard genProc(c, s)
|
||||
genLit(c, newSymNode(n[namePos].sym), dest)
|
||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
||||
let
|
||||
tmp0 = c.genx(n[0])
|
||||
tmp1 = c.genx(n[1])
|
||||
tmp2 = c.genx(n[2])
|
||||
c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
|
||||
c.freeTemp(tmp1)
|
||||
c.freeTemp(tmp2)
|
||||
if dest >= 0:
|
||||
gABC(c, n, whichAsgnOpc(n), dest, tmp0)
|
||||
c.freeTemp(tmp0)
|
||||
if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}:
|
||||
genConv(c, n, n[0], dest, flags)
|
||||
else:
|
||||
dest = tmp0
|
||||
let
|
||||
tmp0 = c.genx(n[0])
|
||||
tmp1 = c.genx(n[1])
|
||||
tmp2 = c.genx(n[2])
|
||||
c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
|
||||
c.freeTemp(tmp1)
|
||||
c.freeTemp(tmp2)
|
||||
if dest >= 0:
|
||||
gABC(c, n, whichAsgnOpc(n), dest, tmp0)
|
||||
c.freeTemp(tmp0)
|
||||
else:
|
||||
dest = tmp0
|
||||
of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
|
||||
nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
|
||||
nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef:
|
||||
@@ -2252,7 +2286,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest)
|
||||
of nkCast:
|
||||
if allowCast in c.features:
|
||||
genConv(c, n, n[1], dest, opcCast)
|
||||
genConv(c, n, n[1], dest, flags, opcCast)
|
||||
else:
|
||||
genCastIntFloat(c, n, dest)
|
||||
of nkTypeOfExpr:
|
||||
|
||||
@@ -27,6 +27,7 @@ from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs
|
||||
from std/os import getAppFilename
|
||||
from std/private/oscommon import dirExists, fileExists
|
||||
from std/private/osdirs import walkDir, createDir
|
||||
from std/private/ospaths2 import getCurrentDir
|
||||
|
||||
from std/times import cpuTime
|
||||
from std/hashes import hash
|
||||
@@ -341,8 +342,8 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||
## reproducible builds and users need to understand that this runs at CT.
|
||||
## Note that `staticExec` can already do equal amount of damage so it's more
|
||||
## of a semantic issue than a security issue.
|
||||
registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, os.getCurrentDir())
|
||||
registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
|
||||
setResult(a, getCurrentDir())
|
||||
registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
|
||||
let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
|
||||
a.setResult osproc.execCmdEx(getString(a, 0), options).toLit
|
||||
|
||||
@@ -33,13 +33,13 @@ proc dispatch(x: Base, params: ...) =
|
||||
dispatchObject,
|
||||
newIntNode(nkIntLit, index)
|
||||
)
|
||||
getVTableCall.typ = base.typ
|
||||
getVTableCall.typ() = base.typ
|
||||
var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
|
||||
var castNode = newTree(nkCast,
|
||||
newNodeIT(nkType, base.info, base.typ),
|
||||
getVTableCall)
|
||||
|
||||
castNode.typ = base.typ
|
||||
castNode.typ() = base.typ
|
||||
vTableCall.add castNode
|
||||
for col in 1..<paramLen:
|
||||
let param = base.typ.n[col].sym
|
||||
|
||||
@@ -20,4 +20,5 @@ when defined(nimStrictMode):
|
||||
switch("hintAsError", "ConvFromXtoItselfNotNeeded")
|
||||
# future work: XDeclaredButNotUsed
|
||||
|
||||
switch("define", "nimVersion:" & NimVersion)
|
||||
switch("define", "nimVersion:" & NimVersion) # deadcode
|
||||
switch("experimental", "strictDefs")
|
||||
|
||||
@@ -21,4 +21,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
[ MIT license: https://www.opensource.org/licenses/mit-license.php ]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user