mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 11:23:40 +00:00
Compare commits
516 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28452442e3 | ||
|
|
6656084004 | ||
|
|
0486a2df51 | ||
|
|
6543040d40 | ||
|
|
0f7b378467 | ||
|
|
46d4079357 | ||
|
|
cd69f37f3a | ||
|
|
79ddb7d89e | ||
|
|
7cb8165e75 | ||
|
|
b539adf829 | ||
|
|
01c084077e | ||
|
|
39be9b981d | ||
|
|
9becd1453d | ||
|
|
f608e109c9 | ||
|
|
4c6d9b6068 | ||
|
|
5da72efbde | ||
|
|
d5549a3c65 | ||
|
|
a57b6d8406 | ||
|
|
2679b3221c | ||
|
|
cc4c7377b2 | ||
|
|
92468e99f7 | ||
|
|
839cbeb371 | ||
|
|
809662a228 | ||
|
|
6f73094263 | ||
|
|
861ebc0f19 | ||
|
|
1d08c4e241 | ||
|
|
cfefd1d95b | ||
|
|
d54b5f3ae1 | ||
|
|
5079074b1a | ||
|
|
99a222d63d | ||
|
|
3e9a66599a | ||
|
|
ce6a34597d | ||
|
|
7af4e3eefd | ||
|
|
130eac2f93 | ||
|
|
b8ce11dd9d | ||
|
|
b7c02e9bad | ||
|
|
544c26c0b8 | ||
|
|
c449c72498 | ||
|
|
1eae14a3be | ||
|
|
5abd21dfa5 | ||
|
|
f009ea6c3e | ||
|
|
8f3bdb6951 | ||
|
|
31d64b57d5 | ||
|
|
2be0721236 | ||
|
|
f191ba8ddd | ||
|
|
fb4a82f5cc | ||
|
|
c0fa86872b | ||
|
|
1ef81f4190 | ||
|
|
3962264c35 | ||
|
|
7c65d9e747 | ||
|
|
c4c51d7e78 | ||
|
|
440b55a44a | ||
|
|
02609f1872 | ||
|
|
483389d399 | ||
|
|
cc49bf07fe | ||
|
|
f4497c6158 | ||
|
|
fed0053481 | ||
|
|
9f74712ec6 | ||
|
|
3e2852cb1b | ||
|
|
ceaa7fb4e8 | ||
|
|
d85c0324b7 | ||
|
|
6938fce40c | ||
|
|
e958f4a3cd | ||
|
|
3f48576113 | ||
|
|
87ee9c84cb | ||
|
|
16394c3772 | ||
|
|
41ce86b577 | ||
|
|
51a9ada043 | ||
|
|
40fe59b6ef | ||
|
|
8b9972c8b6 | ||
|
|
cdb750c962 | ||
|
|
c49fb5ac5f | ||
|
|
a77d1cc6c1 | ||
|
|
ff9cae896c | ||
|
|
bf2395a62e | ||
|
|
d60e0211bc | ||
|
|
88da5e8cee | ||
|
|
d73f478bdc | ||
|
|
f90951cc61 | ||
|
|
af6be4f839 | ||
|
|
49e66e80f0 | ||
|
|
4f09675d8a | ||
|
|
76d07e8caa | ||
|
|
5ba279276e | ||
|
|
c8456eacd5 | ||
|
|
34bb37ddda | ||
|
|
08d74a1c27 | ||
|
|
8ea8755cc0 | ||
|
|
87dc1820c0 | ||
|
|
065c4b443b | ||
|
|
0a8f618e2b | ||
|
|
d472022a77 | ||
|
|
e2a294504e | ||
|
|
b527db9ddd | ||
|
|
c6352ce0ab | ||
|
|
53bb0b591a | ||
|
|
a0b3048f3f | ||
|
|
02e3487c9c | ||
|
|
e194c7cc87 | ||
|
|
161b321796 | ||
|
|
9b527a51b8 | ||
|
|
bb93b39b58 | ||
|
|
cd806f9dbe | ||
|
|
08d51e5c88 | ||
|
|
5b5cd7fa67 | ||
|
|
8e57a9f623 | ||
|
|
478773ffb1 | ||
|
|
f4ebabb9b3 | ||
|
|
611b8bbf67 | ||
|
|
7e2df41850 | ||
|
|
9c1e3bf8fb | ||
|
|
30d4f7791d | ||
|
|
6ab532fd0f | ||
|
|
08642ffe34 | ||
|
|
370ee61f6d | ||
|
|
36f8cefa85 | ||
|
|
fbdc9a4c19 | ||
|
|
6bdb069a66 | ||
|
|
7e6fa9e2d6 | ||
|
|
3ce38f2959 | ||
|
|
b6491e7de5 | ||
|
|
97a6f42b56 | ||
|
|
334848f3ae | ||
|
|
aba9361510 | ||
|
|
c22bfe6bc0 | ||
|
|
8e5ed5dbb7 | ||
|
|
7701b3c7e6 | ||
|
|
151b903172 | ||
|
|
56bb451c6d | ||
|
|
638a8bf84d | ||
|
|
ffb993d5bd | ||
|
|
d27c2159fa | ||
|
|
4fbf538b66 | ||
|
|
9d0c0b89f2 | ||
|
|
7a53db6874 | ||
|
|
dd7cecdbd4 | ||
|
|
807840dc5b | ||
|
|
f80a076588 | ||
|
|
db5973361f | ||
|
|
241edaf0c0 | ||
|
|
8752392838 | ||
|
|
a09da96c65 | ||
|
|
3c0446b082 | ||
|
|
c3f64fb127 | ||
|
|
e855019f84 | ||
|
|
1e602490e9 | ||
|
|
71c5a4f72c | ||
|
|
ade500b2cb | ||
|
|
c1e6cf812f | ||
|
|
091fb5057b | ||
|
|
d2fee7dbab | ||
|
|
8080610248 | ||
|
|
6c2f78a19f | ||
|
|
6f5e5811fc | ||
|
|
42a4adb4a5 | ||
|
|
59ceff4f1a | ||
|
|
b50ab7a5c9 | ||
|
|
433b725cbb | ||
|
|
82553384d1 | ||
|
|
8b82f5de38 | ||
|
|
98ec87d65e | ||
|
|
f56568d851 | ||
|
|
b5b7a127fd | ||
|
|
b61a614e8a | ||
|
|
0506d5b973 | ||
|
|
8518cf079f | ||
|
|
d7b1f0a99a | ||
|
|
eea4ce0e2c | ||
|
|
8c9a645bdf | ||
|
|
5dcfd8d7bb | ||
|
|
d966ee3fc3 | ||
|
|
dc100c5caa | ||
|
|
7f0e07492f | ||
|
|
9c2593444a | ||
|
|
525d64fe88 | ||
|
|
8bc8d40778 | ||
|
|
032da90ed1 | ||
|
|
5aaba213d4 | ||
|
|
af9219ada7 | ||
|
|
3d14381473 | ||
|
|
9f359e8d6d | ||
|
|
3f9c269013 | ||
|
|
11e4bd668c | ||
|
|
e7f73bfebe | ||
|
|
c06bb6cc03 | ||
|
|
4d9e5e8b6d | ||
|
|
1ef9a656d2 | ||
|
|
334f96c05a | ||
|
|
0cba752c8a | ||
|
|
4d075dc301 | ||
|
|
97d819a251 | ||
|
|
b961ee69aa | ||
|
|
42df731a2d | ||
|
|
520bbaf384 | ||
|
|
f58cd51fc4 | ||
|
|
897126a711 | ||
|
|
d4098e6ca0 | ||
|
|
918f972369 | ||
|
|
51166ab382 | ||
|
|
40a1ec21d7 | ||
|
|
29a2e25d1e | ||
|
|
a625fab098 | ||
|
|
052ceca3c1 | ||
|
|
10c9ebad93 | ||
|
|
26b86c8f4d | ||
|
|
2ed45eb848 | ||
|
|
73aeac81d1 | ||
|
|
5bcd9a329a | ||
|
|
4352fa2ef0 | ||
|
|
3617d2e077 | ||
|
|
f9c8775783 | ||
|
|
0f5732bc8c | ||
|
|
e0a4876981 | ||
|
|
ecdcffed4b | ||
|
|
58b1f28177 | ||
|
|
73112d64a3 | ||
|
|
b82d7e8ba1 | ||
|
|
ddd83f8d8a | ||
|
|
8e36fb0fec | ||
|
|
d573578b28 | ||
|
|
909f3b8b79 | ||
|
|
d15705e05b | ||
|
|
0b9ed84d32 | ||
|
|
fcba14707a | ||
|
|
482662d198 | ||
|
|
9ace1f97ac | ||
|
|
1d32607575 | ||
|
|
7c5d005510 | ||
|
|
2b699bca53 | ||
|
|
fb93295344 | ||
|
|
9ebfa7973a | ||
|
|
4f32624641 | ||
|
|
dfa482e292 | ||
|
|
82891e6850 | ||
|
|
850f327713 | ||
|
|
38ad336c69 | ||
|
|
a7711d452d | ||
|
|
e2e7790779 | ||
|
|
dfd2987118 | ||
|
|
e2d4791229 | ||
|
|
ccb40024c6 | ||
|
|
f8294ce06e | ||
|
|
b8302cdd97 | ||
|
|
dfab30734b | ||
|
|
569d02e212 | ||
|
|
e39d152b89 | ||
|
|
7e8a650729 | ||
|
|
0cb4cd159e | ||
|
|
7ecb35115b | ||
|
|
b421d0f8ee | ||
|
|
c452275e29 | ||
|
|
a18dcca744 | ||
|
|
49dfc3a0d4 | ||
|
|
16280d4e49 | ||
|
|
a7a8e364ea | ||
|
|
d94e535145 | ||
|
|
514a25c9a2 | ||
|
|
e449813c61 | ||
|
|
93fb219f10 | ||
|
|
1f8da3835f | ||
|
|
1af88a2d20 | ||
|
|
91e8e605d0 | ||
|
|
f0b5bf359e | ||
|
|
1f07fdd2dc | ||
|
|
ebeef1067f | ||
|
|
510ac84518 | ||
|
|
b7d8896d00 | ||
|
|
a5cc33c1d3 | ||
|
|
b211ada273 | ||
|
|
1a7bc6d878 | ||
|
|
e6f6c369ff | ||
|
|
485b414fce | ||
|
|
7695d51fc4 | ||
|
|
e2bed72b72 | ||
|
|
0861dabfa7 | ||
|
|
647c6687f1 | ||
|
|
af5fd3fea3 | ||
|
|
cab3342a2d | ||
|
|
8c3e62e6de | ||
|
|
95b1dda1db | ||
|
|
1f9cac1f5c | ||
|
|
67f9bc2f4b | ||
|
|
d6d28a9c79 | ||
|
|
6d59680217 | ||
|
|
2f402fcb82 | ||
|
|
793baf34ff | ||
|
|
6481482e0e | ||
|
|
2af9ddc286 | ||
|
|
70d057fcc6 | ||
|
|
8d0e853e0a | ||
|
|
d83ff81695 | ||
|
|
41c447b5f4 | ||
|
|
26ed469996 | ||
|
|
4aff12408c | ||
|
|
8ed0a63973 | ||
|
|
aeeccee50a | ||
|
|
e8bf6af0da | ||
|
|
78835562b1 | ||
|
|
3dda60a8ce | ||
|
|
dcc4e07e54 | ||
|
|
0df351bf50 | ||
|
|
d3b6dba616 | ||
|
|
e1be29942e | ||
|
|
cd220fe3e1 | ||
|
|
86d6f71f5a | ||
|
|
5b9ff963c5 | ||
|
|
65b26401bc | ||
|
|
2f127bf99f | ||
|
|
3c4246dd24 | ||
|
|
fc806710cb | ||
|
|
e2a306355c | ||
|
|
5c71fbab30 | ||
|
|
6bc52737b3 | ||
|
|
f29234b40f | ||
|
|
63c884038d | ||
|
|
986ca7dcd4 | ||
|
|
50ed43df42 | ||
|
|
ce4304ce97 | ||
|
|
f80ce139d5 | ||
|
|
d5c7abe3d2 | ||
|
|
8ce58fab26 | ||
|
|
70b3232d3a | ||
|
|
81d8c0fc17 | ||
|
|
91d1933ea2 | ||
|
|
b9c593404c | ||
|
|
556f217b4c | ||
|
|
d31cce557b | ||
|
|
be4d19e562 | ||
|
|
e0197a8380 | ||
|
|
d2d810585c | ||
|
|
80af252025 | ||
|
|
f485973459 | ||
|
|
f7a461a30c | ||
|
|
f796c01e3c | ||
|
|
9bb7e53e7f | ||
|
|
da9f7f671b | ||
|
|
69e0cdb6c0 | ||
|
|
b529f69518 | ||
|
|
aeb3fe9505 | ||
|
|
d408b94063 | ||
|
|
801733f286 | ||
|
|
6f4106bf5d | ||
|
|
c0861142f8 | ||
|
|
d0288d3b57 | ||
|
|
02fb0476ce | ||
|
|
2e9e7f13ee | ||
|
|
bbf6a62c90 | ||
|
|
6bbf9c3117 | ||
|
|
8f4bfda5f4 | ||
|
|
c3120b6121 | ||
|
|
2529f33760 | ||
|
|
464dc99376 | ||
|
|
3bee04d9f3 | ||
|
|
33dc2367e7 | ||
|
|
ddf5a9f6c5 | ||
|
|
05bba15623 | ||
|
|
5340005869 | ||
|
|
8881017c80 | ||
|
|
dcd0793f2b | ||
|
|
e479151473 | ||
|
|
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 |
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -10,6 +10,16 @@ body:
|
||||
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: nim-version
|
||||
attributes:
|
||||
label: Nim Version
|
||||
description: |
|
||||
Can be obtained from `nim -v` on the command line along with the OS/architecture.
|
||||
For development versions, including the commit hash may help.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
@@ -19,16 +29,6 @@ body:
|
||||
placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: nim-version
|
||||
attributes:
|
||||
label: Nim Version
|
||||
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
|
||||
|
||||
- type: textarea
|
||||
id: current-logs
|
||||
|
||||
11
.github/workflows/bisects.yml
vendored
11
.github/workflows/bisects.yml
vendored
@@ -15,9 +15,16 @@ jobs:
|
||||
name: ${{ matrix.platform }}-bisects
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
- name: Install OpenSSL (Windows)
|
||||
if: |
|
||||
runner.os == 'Windows'
|
||||
run: choco install openssl.light --version=1.1.1.0 # OpenSSL 3.x removed SSL_library_init
|
||||
shell: 'powershell'
|
||||
|
||||
# v2 wont work here, because uses "hardcoded" nim versions, action "dynamically" finds version with bug.
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: 'devel'
|
||||
|
||||
|
||||
4
.github/workflows/ci_docs.yml
vendored
4
.github/workflows/ci_docs.yml
vendored
@@ -43,9 +43,9 @@ jobs:
|
||||
- target: linux
|
||||
os: ubuntu-22.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
os: windows-latest
|
||||
- target: osx
|
||||
os: macos-13
|
||||
os: macos-15
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
14
.github/workflows/ci_packages.yml
vendored
14
.github/workflows/ci_packages.yml
vendored
@@ -18,9 +18,13 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-13]
|
||||
cpu: [amd64]
|
||||
os: [ubuntu-latest, macos-14]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
cpu: amd64
|
||||
- os: macos-14
|
||||
cpu: arm64
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
@@ -41,11 +45,11 @@ jobs:
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-fast update -qq
|
||||
sudo apt-get update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
sudo apt-get install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
valgrind libc6-dbg libblas-dev liblapack-dev libpcre3 xorg-dev
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -68,6 +68,7 @@ testament.db
|
||||
/csources
|
||||
/csources_v1
|
||||
/csources_v2
|
||||
/csources_v3
|
||||
|
||||
/dist/
|
||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
||||
|
||||
@@ -28,24 +28,24 @@ jobs:
|
||||
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_arm64:
|
||||
vmImage: 'macos-15'
|
||||
CPU: arm64
|
||||
OSX_arm64_cpp:
|
||||
vmImage: 'macos-15'
|
||||
CPU: arm64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
# see also: `NIM_TEST_PACKAGES`
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
Windows_amd64_batch1_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
Windows_amd64_batch2_3:
|
||||
vmImage: 'windows-2019'
|
||||
vmImage: 'windows-2025'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "2_3"
|
||||
|
||||
|
||||
35
changelog.md
35
changelog.md
@@ -19,15 +19,48 @@ errors.
|
||||
|
||||
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
|
||||
|
||||
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
|
||||
|
||||
- With `-d:nimPreviewCStringComparisons`, comparsions (`<`, `>`, `<=`, `>=`) between cstrings switch from reference semantics to value semantics like `==` and `!=`.
|
||||
|
||||
- `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it.
|
||||
|
||||
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
|
||||
|
||||
- Adds the switch `--mangle:nim|cpp`, which selects `nim` or `cpp` style name mangling when used with `debuginfo` on, defaults to `nim`. The default is changed from `cpp` to `nim`.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "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.
|
||||
- `strutils.multiReplace` overload for character set replacements in a single pass.
|
||||
Useful for string sanitation. Follows existing multiReplace semantics.
|
||||
|
||||
- `std/files` adds:
|
||||
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
|
||||
- New file operation procs with `Path` support:
|
||||
- `getFilePermissions`, `setFilePermissions` for managing permissions
|
||||
- `tryRemoveFile` for file deletion
|
||||
- `copyFile` with configurable buffer size and symlink handling
|
||||
- `copyFileWithPermissions` to preserve file attributes
|
||||
- `copyFileToDir` for copying files into directories
|
||||
|
||||
- `std/dirs` adds:
|
||||
- New directory operation procs with `Path` support:
|
||||
- `copyDir` with special file handling options
|
||||
- `copyDirWithPermissions` to recursively preserve attributes
|
||||
|
||||
- `system.setLenUninit` now supports refc, JS and VM backends.
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
|
||||
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
|
||||
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
|
||||
|
||||
## Language changes
|
||||
|
||||
@@ -71,4 +104,4 @@ errors.
|
||||
|
||||
## Tool changes
|
||||
|
||||
|
||||
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
|
||||
|
||||
1683
compiler/ast.nim
1683
compiler/ast.nim
File diff suppressed because it is too large
Load Diff
924
compiler/ast2nif.nim
Normal file
924
compiler/ast2nif.nim
Normal file
@@ -0,0 +1,924 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2025 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## AST to NIF bridge.
|
||||
|
||||
import std / [assertions, tables, sets]
|
||||
from std / strutils import startsWith
|
||||
import astdef, idents, msgs, options
|
||||
import lineinfos as astli
|
||||
import pathutils #, modulegraphs
|
||||
import "../dist/nimony/src/lib" / [bitabs, nifstreams, nifcursors, lineinfos,
|
||||
nifindexes, nifreader]
|
||||
import "../dist/nimony/src/gear2" / modnames
|
||||
|
||||
import ic / [enum2nif]
|
||||
|
||||
# ---------------- Line info handling -----------------------------------------
|
||||
|
||||
type
|
||||
LineInfoWriter = object
|
||||
fileK: FileIndex # remember the current pair, even faster than the hash table
|
||||
fileV: FileId
|
||||
tab: Table[FileIndex, FileId]
|
||||
revTab: Table[FileId, FileIndex] # reverse mapping for oldLineInfo
|
||||
man: LineInfoManager
|
||||
config: ConfigRef
|
||||
|
||||
proc get(w: var LineInfoWriter; key: FileIndex): FileId =
|
||||
if w.fileK == key:
|
||||
result = w.fileV
|
||||
else:
|
||||
if key in w.tab:
|
||||
result = w.tab[key]
|
||||
w.fileK = key
|
||||
w.fileV = result
|
||||
else:
|
||||
result = pool.files.getOrIncl(msgs.toFullPath(w.config, key))
|
||||
w.fileK = key
|
||||
w.fileV = result
|
||||
w.tab[key] = result
|
||||
w.revTab[result] = key
|
||||
|
||||
proc nifLineInfo(w: var LineInfoWriter; info: TLineInfo): PackedLineInfo =
|
||||
if info == unknownLineInfo:
|
||||
result = NoLineInfo
|
||||
else:
|
||||
let fid = get(w, info.fileIndex)
|
||||
result = pack(w.man, fid, info.line.int32, info.col)
|
||||
|
||||
proc oldLineInfo(w: var LineInfoWriter; info: PackedLineInfo): TLineInfo =
|
||||
if info == NoLineInfo:
|
||||
result = unknownLineInfo
|
||||
else:
|
||||
var x = unpack(w.man, info)
|
||||
var fileIdx: FileIndex
|
||||
if w.fileV == x.file:
|
||||
fileIdx = w.fileK
|
||||
elif x.file in w.revTab:
|
||||
fileIdx = w.revTab[x.file]
|
||||
else:
|
||||
# Need to look up FileId -> FileIndex via the file path
|
||||
let filePath = pool.files[x.file]
|
||||
fileIdx = msgs.fileInfoIdx(w.config, AbsoluteFile filePath)
|
||||
w.revTab[x.file] = fileIdx
|
||||
result = TLineInfo(line: x.line.uint16, col: x.col.int16, fileIndex: fileIdx)
|
||||
|
||||
|
||||
# -------------- Module name handling --------------------------------------------
|
||||
|
||||
proc modname(moduleToNifSuffix: var Table[FileIndex, string]; module: int; conf: ConfigRef): string =
|
||||
let idx = module.FileIndex
|
||||
# copied from ../nifgen.nim
|
||||
result = moduleToNifSuffix.getOrDefault(idx)
|
||||
if result.len == 0:
|
||||
let fp = toFullPath(conf, idx)
|
||||
result = moduleSuffix(fp, cast[seq[string]](conf.searchPaths))
|
||||
moduleToNifSuffix[idx] = result
|
||||
#echo result, " -> ", fp
|
||||
|
||||
proc modname(moduleToNifSuffix: var Table[FileIndex, string]; module: PSym; conf: ConfigRef): string =
|
||||
assert module.kindImpl == skModule
|
||||
result = modname(moduleToNifSuffix, module.positionImpl, conf)
|
||||
|
||||
|
||||
|
||||
# ------------- Writer ---------------------------------------------------------------
|
||||
|
||||
#[
|
||||
|
||||
Strategy:
|
||||
|
||||
We produce NIF from the PNode structure as the single source of truth. NIF nodes can
|
||||
however, refer to PSym and PType, these get NIF names. If the PSym/PType belongs to
|
||||
the module that we are currently writing, we emit these fields as an inner NIF
|
||||
structure via the special tags `sd` and `td`. In fact it is only these tags
|
||||
that get the NIF `SymbolDef` kinds so that the lazy loading mechanism cannot
|
||||
be confused.
|
||||
|
||||
We could also emit non-local symbols and types later as the index structure
|
||||
will tell us the precise offsets anyway.
|
||||
|
||||
]#
|
||||
|
||||
const
|
||||
hiddenTypeTagName = "ht"
|
||||
symDefTagName = "sd"
|
||||
typeDefTagName = "td"
|
||||
|
||||
let
|
||||
sdefTag = registerTag(symDefTagName)
|
||||
tdefTag = registerTag(typeDefTagName)
|
||||
hiddenTypeTag = registerTag(hiddenTypeTagName)
|
||||
|
||||
type
|
||||
Writer = object
|
||||
deps: TokenBuf # include&import deps
|
||||
infos: LineInfoWriter
|
||||
currentModule: int32
|
||||
decodedFileIndices: HashSet[FileIndex]
|
||||
moduleToNifSuffix: Table[FileIndex, string]
|
||||
locals: HashSet[ItemId] # track proc-local symbols
|
||||
inProc: int
|
||||
|
||||
proc toNifSymName(w: var Writer; sym: PSym): string =
|
||||
## Generate NIF name for a symbol: local names are `ident.disamb`,
|
||||
## global names are `ident.disamb.moduleSuffix`
|
||||
result = sym.name.s
|
||||
result.add '.'
|
||||
result.addInt sym.disamb
|
||||
if sym.itemId notin w.locals:
|
||||
# Global symbol: ident.disamb.moduleSuffix
|
||||
let module = sym.itemId.module
|
||||
result.add '.'
|
||||
result.add modname(w.moduleToNifSuffix, module, w.infos.config)
|
||||
|
||||
type
|
||||
ParsedSymName* = object
|
||||
name*: string
|
||||
module*: string
|
||||
count*: int
|
||||
|
||||
proc parseSymName*(s: string): ParsedSymName =
|
||||
var i = s.len - 2
|
||||
while i > 0:
|
||||
if s[i] == '.':
|
||||
if s[i+1] in {'0'..'9'}:
|
||||
var count = ord(s[i+1]) - ord('0')
|
||||
var j = i+2
|
||||
while j < s.len and s[j] in {'0'..'9'}:
|
||||
count = count * 10 + ord(s[j]) - ord('0')
|
||||
inc j
|
||||
return ParsedSymName(name: substr(s, 0, i-1), module: "", count: count)
|
||||
else:
|
||||
let mend = s.high
|
||||
var b = i-1
|
||||
while b > 0 and s[b] != '.': dec b
|
||||
var j = b+1
|
||||
var count = 0
|
||||
while j < s.len and s[j] in {'0'..'9'}:
|
||||
count = count * 10 + ord(s[j]) - ord('0')
|
||||
inc j
|
||||
|
||||
return ParsedSymName(name: substr(s, 0, b-1), module: substr(s, i+1, mend), count: count)
|
||||
dec i
|
||||
return ParsedSymName(name: s, module: "")
|
||||
|
||||
template buildTree(dest: var TokenBuf; tag: TagId; body: untyped) =
|
||||
dest.addParLe tag
|
||||
body
|
||||
dest.addParRi
|
||||
|
||||
template buildTree(dest: var TokenBuf; tag: string; body: untyped) =
|
||||
buildTree dest, pool.tags.getOrIncl(tag), body
|
||||
|
||||
proc writeFlags[E](dest: var TokenBuf; flags: set[E]) =
|
||||
var flagsAsIdent = ""
|
||||
genFlags(flags, flagsAsIdent)
|
||||
if flagsAsIdent.len > 0:
|
||||
dest.addIdent flagsAsIdent
|
||||
else:
|
||||
dest.addDotToken
|
||||
|
||||
proc trLineInfo(w: var Writer; info: TLineInfo): PackedLineInfo {.inline.} =
|
||||
result = nifLineInfo(w.infos, info)
|
||||
|
||||
proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode)
|
||||
proc writeType(w: var Writer; dest: var TokenBuf; typ: PType)
|
||||
proc writeSym(w: var Writer; dest: var TokenBuf; sym: PSym)
|
||||
|
||||
proc typeToNifSym(w: var Writer; typ: PType): string =
|
||||
result = "`t"
|
||||
result.addInt ord(typ.kind)
|
||||
result.add '.'
|
||||
result.addInt typ.uniqueId.item
|
||||
result.add '.'
|
||||
result.add modname(w.moduleToNifSuffix, typ.uniqueId.module, w.infos.config)
|
||||
|
||||
proc writeLoc(w: var Writer; dest: var TokenBuf; loc: TLoc) =
|
||||
dest.addIdent toNifTag(loc.k)
|
||||
dest.addIdent toNifTag(loc.storage)
|
||||
writeFlags(dest, loc.flags) # TLocFlags
|
||||
dest.addStrLit loc.snippet
|
||||
|
||||
proc writeTypeDef(w: var Writer; dest: var TokenBuf; typ: PType) =
|
||||
dest.buildTree tdefTag:
|
||||
dest.addSymDef pool.syms.getOrIncl(w.typeToNifSym(typ)), NoLineInfo
|
||||
|
||||
#dest.addIdent toNifTag(typ.kind)
|
||||
writeFlags(dest, typ.flagsImpl)
|
||||
dest.addIdent toNifTag(typ.callConvImpl)
|
||||
dest.addIntLit typ.sizeImpl
|
||||
dest.addIntLit typ.alignImpl
|
||||
dest.addIntLit typ.paddingAtEndImpl
|
||||
dest.addIntLit typ.itemId.item # nonUniqueId
|
||||
|
||||
writeType(w, dest, typ.typeInstImpl)
|
||||
writeNode(w, dest, typ.nImpl)
|
||||
writeSym(w, dest, typ.ownerFieldImpl)
|
||||
writeSym(w, dest, typ.symImpl)
|
||||
|
||||
# Write TLoc structure
|
||||
writeLoc w, dest, typ.locImpl
|
||||
# we store the type's elements here at the end so that
|
||||
# it is not ambiguous and saves space:
|
||||
for ch in typ.sonsImpl:
|
||||
writeType(w, dest, ch)
|
||||
|
||||
|
||||
proc writeType(w: var Writer; dest: var TokenBuf; typ: PType) =
|
||||
if typ == nil:
|
||||
dest.addDotToken()
|
||||
elif typ.itemId.module == w.currentModule and typ.state == Complete:
|
||||
typ.state = Sealed
|
||||
writeTypeDef(w, dest, typ)
|
||||
else:
|
||||
dest.addSymUse pool.syms.getOrIncl(w.typeToNifSym(typ)), NoLineInfo
|
||||
|
||||
proc writeBool(dest: var TokenBuf; b: bool) =
|
||||
dest.buildTree (if b: "true" else: "false"):
|
||||
discard
|
||||
|
||||
proc writeLib(w: var Writer; dest: var TokenBuf; lib: PLib) =
|
||||
if lib == nil:
|
||||
dest.addDotToken()
|
||||
else:
|
||||
dest.buildTree toNifTag(lib.kind):
|
||||
dest.writeBool lib.generated
|
||||
dest.writeBool lib.isOverridden
|
||||
dest.addStrLit lib.name
|
||||
writeNode w, dest, lib.path
|
||||
|
||||
proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
|
||||
dest.addParLe sdefTag, trLineInfo(w, sym.infoImpl)
|
||||
dest.addSymDef pool.syms.getOrIncl(w.toNifSymName(sym)), NoLineInfo
|
||||
if sfExported in sym.flagsImpl:
|
||||
dest.addIdent "x"
|
||||
else:
|
||||
dest.addDotToken
|
||||
if sym.magicImpl == mNone:
|
||||
dest.addDotToken
|
||||
else:
|
||||
dest.addIdent toNifTag(sym.magicImpl)
|
||||
writeFlags(dest, sym.flagsImpl)
|
||||
writeFlags(dest, sym.optionsImpl)
|
||||
dest.addIntLit sym.offsetImpl
|
||||
# field `disamb` made part of the name, so do not store it here
|
||||
dest.buildTree sym.kindImpl.toNifTag:
|
||||
case sym.kindImpl
|
||||
of skLet, skVar, skField, skForVar:
|
||||
writeSym(w, dest, sym.guardImpl)
|
||||
dest.addIntLit sym.bitsizeImpl
|
||||
dest.addIntLit sym.alignmentImpl
|
||||
else:
|
||||
discard
|
||||
if sym.kindImpl == skModule:
|
||||
dest.addDotToken() # position will be set by the loader!
|
||||
else:
|
||||
dest.addIntLit sym.positionImpl
|
||||
writeType(w, dest, sym.typImpl)
|
||||
writeSym(w, dest, sym.ownerFieldImpl)
|
||||
# We do not store `sym.ast` here but instead set it in the deserializer
|
||||
#writeNode(w, sym.ast)
|
||||
writeLoc w, dest, sym.locImpl
|
||||
writeNode(w, dest, sym.constraintImpl)
|
||||
writeSym(w, dest, sym.instantiatedFromImpl)
|
||||
dest.addParRi
|
||||
|
||||
proc writeSym(w: var Writer; dest: var TokenBuf; sym: PSym) =
|
||||
if sym == nil:
|
||||
dest.addDotToken()
|
||||
elif sym.itemId.module == w.currentModule and sym.state == Complete:
|
||||
sym.state = Sealed
|
||||
writeSymDef(w, dest, sym)
|
||||
else:
|
||||
# NIF has direct support for symbol references so we don't need to use a tag here,
|
||||
# unlike what we do for types!
|
||||
dest.addSymUse pool.syms.getOrIncl(w.toNifSymName(sym)), NoLineInfo
|
||||
|
||||
proc writeSymNode(w: var Writer; dest: var TokenBuf; n: PNode; sym: PSym) =
|
||||
if sym == nil:
|
||||
dest.addDotToken()
|
||||
elif sym.itemId.module == w.currentModule and sym.state == Complete:
|
||||
sym.state = Sealed
|
||||
if n.typField != n.sym.typImpl:
|
||||
dest.buildTree hiddenTypeTag, trLineInfo(w, n.info):
|
||||
writeType(w, dest, n.typField)
|
||||
writeSymDef(w, dest, sym)
|
||||
else:
|
||||
writeSymDef(w, dest, sym)
|
||||
else:
|
||||
# NIF has direct support for symbol references so we don't need to use a tag here,
|
||||
# unlike what we do for types!
|
||||
let info = trLineInfo(w, n.info)
|
||||
if n.typField != n.sym.typImpl:
|
||||
dest.buildTree hiddenTypeTag, info:
|
||||
writeType(w, dest, n.typField)
|
||||
dest.addSymUse pool.syms.getOrIncl(w.toNifSymName(sym)), info
|
||||
else:
|
||||
dest.addSymUse pool.syms.getOrIncl(w.toNifSymName(sym)), info
|
||||
|
||||
proc writeNodeFlags(dest: var TokenBuf; flags: set[TNodeFlag]) {.inline.} =
|
||||
writeFlags(dest, flags)
|
||||
|
||||
template withNode(w: var Writer; dest: var TokenBuf; n: PNode; body: untyped) =
|
||||
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)
|
||||
writeNodeFlags(dest, n.flags)
|
||||
writeType(w, dest, n.typField)
|
||||
body
|
||||
dest.addParRi
|
||||
|
||||
proc addLocalSym(w: var Writer; n: PNode) =
|
||||
## Add symbol from a node to locals set if it's a symbol node
|
||||
if n != nil and n.kind == nkSym and n.sym != nil and w.inProc > 0:
|
||||
w.locals.incl(n.sym.itemId)
|
||||
|
||||
proc addLocalSyms(w: var Writer; n: PNode) =
|
||||
if n.kind in {nkIdentDefs, nkVarTuple}:
|
||||
# nkIdentDefs: [ident1, ident2, ..., type, default]
|
||||
# All children except the last two are identifiers
|
||||
for i in 0 ..< max(0, n.len - 2):
|
||||
addLocalSyms(w, n[i])
|
||||
elif n.kind == nkSym:
|
||||
addLocalSym(w, n)
|
||||
|
||||
proc trInclude(w: var Writer; n: PNode) =
|
||||
w.deps.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)
|
||||
for child in n:
|
||||
assert child.kind == nkStrLit
|
||||
w.deps.addStrLit child.strVal
|
||||
w.deps.addParRi
|
||||
|
||||
proc trImport(w: var Writer; n: PNode) =
|
||||
for child in n:
|
||||
if child.kind == nkSym:
|
||||
w.deps.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)
|
||||
let s = child.sym
|
||||
assert s.kindImpl == skModule
|
||||
let fp = toFullPath(w.infos.config, s.positionImpl.FileIndex)
|
||||
w.deps.addStrLit fp
|
||||
w.deps.addParRi
|
||||
|
||||
proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode) =
|
||||
if n == nil:
|
||||
dest.addDotToken
|
||||
else:
|
||||
case n.kind
|
||||
of nkEmpty, nkNone:
|
||||
let info = trLineInfo(w, n.info)
|
||||
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), info
|
||||
dest.addParRi
|
||||
of nkIdent:
|
||||
# nkIdent uses flags and typ when it is a generic parameter
|
||||
w.withNode dest, n:
|
||||
dest.addIdent n.ident.s
|
||||
of nkSym:
|
||||
writeSymNode(w, dest, n, n.sym)
|
||||
of nkCharLit:
|
||||
w.withNode dest, n:
|
||||
dest.add charToken(n.intVal.char, NoLineInfo)
|
||||
of nkIntLit .. nkInt64Lit:
|
||||
w.withNode dest, n:
|
||||
dest.addIntLit n.intVal
|
||||
of nkUIntLit .. nkUInt64Lit:
|
||||
w.withNode dest, n:
|
||||
dest.addUIntLit cast[BiggestUInt](n.intVal)
|
||||
of nkFloatLit .. nkFloat128Lit:
|
||||
w.withNode dest, n:
|
||||
dest.add floatToken(pool.floats.getOrIncl(n.floatVal), NoLineInfo)
|
||||
of nkStrLit .. nkTripleStrLit:
|
||||
w.withNode dest, n:
|
||||
dest.addStrLit n.strVal
|
||||
of nkNilLit:
|
||||
w.withNode dest, n:
|
||||
discard
|
||||
of nkLetSection, nkVarSection, nkConstSection, nkGenericParams:
|
||||
# Track local variables declared in let/var sections
|
||||
w.withNode dest, n:
|
||||
for child in n:
|
||||
addLocalSyms w, child
|
||||
# Process the child node
|
||||
writeNode(w, dest, child)
|
||||
of nkForStmt, nkTypeDef:
|
||||
# Track for loop variable (first child is the loop variable)
|
||||
w.withNode dest, n:
|
||||
if n.len > 0:
|
||||
addLocalSyms(w, n[0])
|
||||
for i in 0 ..< n.len:
|
||||
writeNode(w, dest, n[i])
|
||||
of nkFormalParams:
|
||||
# Track parameters (first child is return type, rest are parameters)
|
||||
w.withNode dest, n:
|
||||
for i in 0 ..< n.len:
|
||||
if i > 0: # Skip return type
|
||||
addLocalSyms(w, n[i])
|
||||
writeNode(w, dest, n[i])
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkLambda, nkDo, nkMacroDef:
|
||||
inc w.inProc
|
||||
# Entering a proc/function body - parameters are local
|
||||
var ast = n
|
||||
if n[namePos].kind == nkSym:
|
||||
ast = n[namePos].sym.astImpl
|
||||
if ast == nil: ast = n
|
||||
w.withNode dest, ast:
|
||||
# Process body and other parts
|
||||
for i in 0 ..< ast.len:
|
||||
writeNode(w, dest, ast[i])
|
||||
dec w.inProc
|
||||
of nkImportStmt:
|
||||
# this has been transformed for us, see `importer.nim` to contain a list of module syms:
|
||||
trImport w, n
|
||||
of nkIncludeStmt:
|
||||
trInclude w, n
|
||||
else:
|
||||
w.withNode dest, n:
|
||||
for i in 0 ..< n.len:
|
||||
writeNode(w, dest, n[i])
|
||||
|
||||
proc writeToplevelNode(w: var Writer; outer, inner: var TokenBuf; n: PNode) =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for son in n: writeToplevelNode(w, outer, inner, son)
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkLambda, nkDo, nkMacroDef:
|
||||
# Delegate to `w.topLevel`!
|
||||
writeNode w, inner, n
|
||||
of nkConstSection, nkTypeSection, nkTypeDef:
|
||||
writeNode w, inner, n
|
||||
else:
|
||||
writeNode w, outer, n
|
||||
|
||||
proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode) =
|
||||
var w = Writer(infos: LineInfoWriter(config: config), currentModule: thisModule)
|
||||
var outer = createTokenBuf(300)
|
||||
var inner = createTokenBuf(300)
|
||||
|
||||
let rootInfo = trLineInfo(w, n.info)
|
||||
outer.addParLe pool.tags.getOrIncl(toNifTag(nkStmtList)), rootInfo
|
||||
inner.addParLe pool.tags.getOrIncl(toNifTag(nkStmtList)), rootInfo
|
||||
|
||||
w.writeToplevelNode outer, inner, n
|
||||
|
||||
outer.addParRi()
|
||||
inner.addParRi()
|
||||
|
||||
let m = modname(w.moduleToNifSuffix, w.currentModule, w.infos.config)
|
||||
let nifFilename = AbsoluteFile(m).changeFileExt(".nif")
|
||||
let d = completeGeneratedFilePath(config, nifFilename).string
|
||||
|
||||
var dest = createTokenBuf(600)
|
||||
dest.addParLe pool.tags.getOrIncl(toNifTag(nkStmtList)), rootInfo
|
||||
dest.add w.deps
|
||||
dest.add outer
|
||||
dest.add inner
|
||||
dest.addParRi()
|
||||
|
||||
writeFile(dest, d)
|
||||
createIndex(d, false, dest[0].info)
|
||||
|
||||
|
||||
# --------------------------- Loader (lazy!) -----------------------------------------------
|
||||
|
||||
proc nodeKind(n: Cursor): TNodeKind {.inline.} =
|
||||
assert n.kind == ParLe
|
||||
parse(TNodeKind, pool.tags[n.tagId])
|
||||
|
||||
proc expect(n: Cursor; k: set[NifKind]) =
|
||||
if n.kind notin k:
|
||||
when defined(debug):
|
||||
writeStackTrace()
|
||||
quit "[NIF decoder] expected: " & $k & " but got: " & $n.kind & toString n
|
||||
|
||||
proc expect(n: Cursor; k: NifKind) {.inline.} =
|
||||
expect n, {k}
|
||||
|
||||
proc incExpect(n: var Cursor; k: set[NifKind]) =
|
||||
inc n
|
||||
expect n, k
|
||||
|
||||
proc incExpect(n: var Cursor; k: NifKind) {.inline.} =
|
||||
incExpect n, {k}
|
||||
|
||||
proc skipParRi(n: var Cursor) =
|
||||
expect n, {ParRi}
|
||||
inc n
|
||||
|
||||
proc firstSon*(n: Cursor): Cursor {.inline.} =
|
||||
result = n
|
||||
inc result
|
||||
|
||||
proc expectTag(n: Cursor; tagId: TagId) =
|
||||
if n.kind == ParLe and n.tagId == tagId:
|
||||
discard
|
||||
else:
|
||||
when defined(debug):
|
||||
writeStackTrace()
|
||||
if n.kind != ParLe:
|
||||
quit "[NIF decoder] expected: ParLe but got: " & $n.kind & toString n
|
||||
else:
|
||||
quit "[NIF decoder] expected: " & pool.tags[tagId] & " but got: " & pool.tags[n.tagId] & toString n
|
||||
|
||||
proc incExpectTag(n: var Cursor; tagId: TagId) =
|
||||
inc n
|
||||
expectTag(n, tagId)
|
||||
|
||||
proc loadBool(n: var Cursor): bool =
|
||||
if n.kind == ParLe:
|
||||
result = pool.tags[n.tagId] == "true"
|
||||
inc n
|
||||
skipParRi n
|
||||
else:
|
||||
raiseAssert "(true)/(false) expected"
|
||||
|
||||
type
|
||||
NifModule = object
|
||||
stream: nifstreams.Stream
|
||||
symCounter: int32
|
||||
index: NifIndex
|
||||
|
||||
DecodeContext* = object
|
||||
infos: LineInfoWriter
|
||||
#moduleIds: Table[string, int32]
|
||||
types: Table[ItemId, (PType, NifIndexEntry)]
|
||||
syms: Table[ItemId, (PSym, NifIndexEntry)]
|
||||
mods: seq[NifModule]
|
||||
cache: IdentCache
|
||||
#moduleToNifSuffix: Table[FileIndex, string]
|
||||
|
||||
proc createDecodeContext*(config: ConfigRef; cache: IdentCache): DecodeContext =
|
||||
## Supposed to be a global variable
|
||||
result = DecodeContext(infos: LineInfoWriter(config: config), cache: cache)
|
||||
|
||||
proc cursorFromIndexEntry(c: var DecodeContext; module: FileIndex; entry: NifIndexEntry;
|
||||
buf: var TokenBuf): Cursor =
|
||||
let s = addr c.mods[module.int32].stream
|
||||
s.r.jumpTo entry.offset
|
||||
var buf = createTokenBuf(30)
|
||||
nifcursors.parse(s[], buf, entry.info)
|
||||
result = cursorAt(buf, 0)
|
||||
|
||||
proc moduleId(c: var DecodeContext; suffix: string): FileIndex =
|
||||
var isKnownFile = false
|
||||
result = c.infos.config.registerNifSuffix(suffix, isKnownFile)
|
||||
if not isKnownFile:
|
||||
let modFile = (getNimcacheDir(c.infos.config) / RelativeFile(suffix & ".nif")).string
|
||||
let idxFile = (getNimcacheDir(c.infos.config) / RelativeFile(suffix & ".idx.nif")).string
|
||||
if result.int >= c.mods.len:
|
||||
c.mods.setLen(result.int + 1)
|
||||
c.mods[result.int] = NifModule(stream: nifstreams.open(modFile), index: readIndex(idxFile))
|
||||
|
||||
proc getOffset(c: var DecodeContext; module: FileIndex; nifName: string): NifIndexEntry =
|
||||
let ii = addr c.mods[module.int32].index
|
||||
result = ii.public.getOrDefault(nifName)
|
||||
if result.offset == 0:
|
||||
result = ii.private.getOrDefault(nifName)
|
||||
if result.offset == 0:
|
||||
raiseAssert "symbol has no offset: " & nifName
|
||||
|
||||
proc loadNode(c: var DecodeContext; n: var Cursor): PNode
|
||||
|
||||
proc loadTypeStub(c: var DecodeContext; t: SymId): PType =
|
||||
let name = pool.syms[t]
|
||||
assert name.startsWith("`t")
|
||||
var i = len("`t")
|
||||
var k = 0
|
||||
while i < name.len and name[i] in {'0'..'9'}:
|
||||
k = k * 10 + name[i].ord - ord('0')
|
||||
inc i
|
||||
if i < name.len and name[i] == '.': inc i
|
||||
var itemId = 0'i32
|
||||
while i < name.len and name[i] in {'0'..'9'}:
|
||||
itemId = itemId * 10'i32 + int32(name[i].ord - ord('0'))
|
||||
inc i
|
||||
if i < name.len and name[i] == '.': inc i
|
||||
let suffix = name.substr(i)
|
||||
let id = ItemId(module: moduleId(c, suffix).int32, item: itemId)
|
||||
result = c.types.getOrDefault(id)[0]
|
||||
if result == nil:
|
||||
let offs = c.getOffset(id.module.FileIndex, name)
|
||||
result = PType(itemId: id, uniqueId: id, kind: TTypeKind(k), state: Partial)
|
||||
c.types[id] = (result, offs)
|
||||
|
||||
proc loadTypeStub(c: var DecodeContext; n: var Cursor): PType =
|
||||
if n.kind == DotToken:
|
||||
result = nil
|
||||
inc n
|
||||
elif n.kind == Symbol:
|
||||
let s = n.symId
|
||||
result = loadTypeStub(c, s)
|
||||
inc n
|
||||
elif n.kind == ParLe and n.tagId == tdefTag:
|
||||
let s = n.firstSon.symId
|
||||
skip n
|
||||
result = loadTypeStub(c, s)
|
||||
else:
|
||||
raiseAssert "type expected but got " & $n.kind
|
||||
|
||||
proc loadSymStub(c: var DecodeContext; t: SymId): PSym =
|
||||
let symAsStr = pool.syms[t]
|
||||
let sn = parseSymName(symAsStr)
|
||||
let module = moduleId(c, sn.module)
|
||||
let val = addr c.mods[module.int32].symCounter
|
||||
inc val[]
|
||||
|
||||
let id = ItemId(module: module.int32, item: val[])
|
||||
result = c.syms.getOrDefault(id)[0]
|
||||
if result == nil:
|
||||
let offs = c.getOffset(module, symAsStr)
|
||||
result = PSym(itemId: id, kindImpl: skStub, name: c.cache.getIdent(sn.name), disamb: sn.count.int32, state: Partial)
|
||||
c.syms[id] = (result, offs)
|
||||
|
||||
proc loadSymStub(c: var DecodeContext; n: var Cursor): PSym =
|
||||
if n.kind == DotToken:
|
||||
result = nil
|
||||
inc n
|
||||
elif n.kind == Symbol:
|
||||
let s = n.symId
|
||||
result = loadSymStub(c, s)
|
||||
inc n
|
||||
elif n.kind == ParLe and n.tagId == sdefTag:
|
||||
let s = n.firstSon.symId
|
||||
skip n
|
||||
result = loadSymStub(c, s)
|
||||
else:
|
||||
raiseAssert "sym expected but got " & $n.kind
|
||||
|
||||
proc isStub*(t: PType): bool {.inline.} = t.state == Partial
|
||||
proc isStub*(s: PSym): bool {.inline.} = s.state == Partial
|
||||
|
||||
proc loadAtom[T](t: typedesc[set[T]]; n: var Cursor): set[T] =
|
||||
if n.kind == DotToken:
|
||||
result = {}
|
||||
inc n
|
||||
else:
|
||||
expect n, Ident
|
||||
result = parse(T, pool.strings[n.litId])
|
||||
inc n
|
||||
|
||||
proc loadAtom[T: enum](t: typedesc[T]; n: var Cursor): T =
|
||||
if n.kind == DotToken:
|
||||
result = default(T)
|
||||
inc n
|
||||
else:
|
||||
expect n, Ident
|
||||
result = parse(T, pool.strings[n.litId])
|
||||
inc n
|
||||
|
||||
proc loadAtom(t: typedesc[string]; n: var Cursor): string =
|
||||
expect n, StringLit
|
||||
result = pool.strings[n.litId]
|
||||
inc n
|
||||
|
||||
proc loadAtom[T: int16|int32|int64](t: typedesc[T]; n: var Cursor): T =
|
||||
expect n, IntLit
|
||||
result = pool.integers[n.intId].T
|
||||
inc n
|
||||
|
||||
template loadField(field) {.dirty.} =
|
||||
field = loadAtom(typeof(field), n)
|
||||
|
||||
proc loadLoc(c: var DecodeContext; n: var Cursor; loc: var TLoc) =
|
||||
loadField loc.k
|
||||
loadField loc.storage
|
||||
loadField loc.flags
|
||||
loadField loc.snippet
|
||||
|
||||
proc loadType*(c: var DecodeContext; t: PType) =
|
||||
if t.state != Partial: return
|
||||
t.state = Sealed
|
||||
var buf = createTokenBuf(30)
|
||||
var n = cursorFromIndexEntry(c, t.itemId.module.FileIndex, c.types[t.itemId][1], buf)
|
||||
|
||||
expect n, ParLe
|
||||
if n.tagId != tdefTag:
|
||||
raiseAssert "(td) expected"
|
||||
inc n
|
||||
expect n, SymbolDef
|
||||
# ignore the type's name, we have already used it to create this PType's itemId!
|
||||
inc n
|
||||
#loadField t.kind
|
||||
loadField t.flagsImpl
|
||||
loadField t.callConvImpl
|
||||
loadField t.sizeImpl
|
||||
loadField t.alignImpl
|
||||
loadField t.paddingAtEndImpl
|
||||
loadField t.itemId.item # nonUniqueId
|
||||
|
||||
t.typeInstImpl = loadTypeStub(c, n)
|
||||
t.nImpl = loadNode(c, n)
|
||||
t.ownerFieldImpl = loadSymStub(c, n)
|
||||
t.symImpl = loadSymStub(c, n)
|
||||
loadLoc c, n, t.locImpl
|
||||
|
||||
while n.kind != ParRi:
|
||||
t.sonsImpl.add loadTypeStub(c, n)
|
||||
|
||||
skipParRi n
|
||||
|
||||
proc loadAnnex(c: var DecodeContext; n: var Cursor): PLib =
|
||||
if n.kind == DotToken:
|
||||
result = nil
|
||||
inc n
|
||||
elif n.kind == ParLe:
|
||||
result = PLib(kind: parse(TLibKind, pool.tags[n.tagId]))
|
||||
inc n
|
||||
result.generated = loadBool(n)
|
||||
result.isOverridden = loadBool(n)
|
||||
expect n, StringLit
|
||||
result.name = pool.strings[n.litId]
|
||||
inc n
|
||||
result.path = loadNode(c, n)
|
||||
skipParRi n
|
||||
else:
|
||||
raiseAssert "`lib/annex` information expected"
|
||||
|
||||
proc loadSym*(c: var DecodeContext; s: PSym) =
|
||||
if s.state != Partial: return
|
||||
s.state = Sealed
|
||||
var buf = createTokenBuf(30)
|
||||
var n = cursorFromIndexEntry(c, s.itemId.module.FileIndex, c.syms[s.itemId][1], buf)
|
||||
|
||||
expect n, ParLe
|
||||
if n.tagId != sdefTag:
|
||||
raiseAssert "(sd) expected"
|
||||
inc n
|
||||
expect n, SymbolDef
|
||||
# ignore the symbol's name, we have already used it to create this PSym instance!
|
||||
inc n
|
||||
if n.kind == Ident:
|
||||
if pool.strings[n.litId] == "x":
|
||||
s.flagsImpl.incl sfExported
|
||||
inc n
|
||||
else:
|
||||
raiseAssert "expected `x` as the export marker"
|
||||
elif n.kind == DotToken:
|
||||
inc n
|
||||
else:
|
||||
raiseAssert "expected `x` or '.' but got " & $n.kind
|
||||
|
||||
loadField s.magicImpl
|
||||
loadField s.flagsImpl
|
||||
loadField s.optionsImpl
|
||||
loadField s.offsetImpl
|
||||
|
||||
expect n, ParLe
|
||||
s.kindImpl = parse(TSymKind, pool.tags[n.tagId])
|
||||
inc n
|
||||
|
||||
case s.kindImpl
|
||||
of skLet, skVar, skField, skForVar:
|
||||
s.guardImpl = loadSymStub(c, n)
|
||||
loadField s.bitsizeImpl
|
||||
loadField s.alignmentImpl
|
||||
else:
|
||||
discard
|
||||
skipParRi n
|
||||
|
||||
if s.kindImpl == skModule:
|
||||
expect n, DotToken
|
||||
inc n
|
||||
else:
|
||||
loadField s.positionImpl
|
||||
s.typImpl = loadTypeStub(c, n)
|
||||
s.ownerFieldImpl = loadSymStub(c, n)
|
||||
# We do not store `sym.ast` here but instead set it in the deserializer
|
||||
#writeNode(w, sym.ast)
|
||||
loadLoc c, n, s.locImpl
|
||||
s.constraintImpl = loadNode(c, n)
|
||||
s.instantiatedFromImpl = loadSymStub(c, n)
|
||||
skipParRi n
|
||||
|
||||
|
||||
template withNode(c: var DecodeContext; n: var Cursor; result: PNode; kind: TNodeKind; body: untyped) =
|
||||
let info = c.infos.oldLineInfo(n.info)
|
||||
let flags = loadAtom(TNodeFlags, n)
|
||||
result = newNodeI(kind, info)
|
||||
result.flags = flags
|
||||
result.typField = c.loadTypeStub n
|
||||
body
|
||||
skipParRi n
|
||||
|
||||
proc loadNode(c: var DecodeContext; n: var Cursor): PNode =
|
||||
result = nil
|
||||
case n.kind:
|
||||
of DotToken:
|
||||
result = nil
|
||||
inc n
|
||||
of ParLe:
|
||||
let kind = n.nodeKind
|
||||
case kind:
|
||||
of nkNone:
|
||||
# special NIF introduced tag?
|
||||
case pool.tags[n.tagId]
|
||||
of hiddenTypeTagName:
|
||||
inc n
|
||||
let typ = c.loadTypeStub n
|
||||
let info = c.infos.oldLineInfo(n.info)
|
||||
result = newSymNode(c.loadSymStub n, info)
|
||||
result.typField = typ
|
||||
skipParRi n
|
||||
of symDefTagName:
|
||||
let name = n.firstSon
|
||||
assert name.kind == SymbolDef
|
||||
result = newSymNode(c.loadSymStub name.symId, c.infos.oldLineInfo(n.info))
|
||||
skip n
|
||||
of typeDefTagName:
|
||||
raiseAssert "`td` tag in invalid context"
|
||||
of "none":
|
||||
result = newNodeI(nkNone, c.infos.oldLineInfo(n.info))
|
||||
result.flags = loadAtom(TNodeFlags, n)
|
||||
skipParRi n
|
||||
else:
|
||||
raiseAssert "Unknown NIF tag " & pool.tags[n.tagId]
|
||||
of nkEmpty:
|
||||
result = newNodeI(nkEmpty, c.infos.oldLineInfo(n.info))
|
||||
result.flags = loadAtom(TNodeFlags, n)
|
||||
skipParRi n
|
||||
of nkIdent:
|
||||
let info = c.infos.oldLineInfo(n.info)
|
||||
let flags = loadAtom(TNodeFlags, n)
|
||||
let typ = c.loadTypeStub n
|
||||
expect n, Ident
|
||||
result = newIdentNode(c.cache.getIdent(pool.strings[n.litId]), info)
|
||||
inc n
|
||||
result.flags = flags
|
||||
result.typField = typ
|
||||
skipParRi n
|
||||
of nkSym:
|
||||
let info = c.infos.oldLineInfo(n.info)
|
||||
result = newSymNode(c.loadSymStub n, info)
|
||||
of nkCharLit:
|
||||
c.withNode n, result, kind:
|
||||
expect n, CharLit
|
||||
result.intVal = n.charLit.int
|
||||
inc n
|
||||
of nkIntLit .. nkInt64Lit:
|
||||
c.withNode n, result, kind:
|
||||
expect n, IntLit
|
||||
result.intVal = pool.integers[n.intId]
|
||||
inc n
|
||||
of nkUIntLit .. nkUInt64Lit:
|
||||
c.withNode n, result, kind:
|
||||
expect n, UIntLit
|
||||
result.intVal = cast[BiggestInt](pool.uintegers[n.uintId])
|
||||
inc n
|
||||
of nkFloatLit .. nkFloat128Lit:
|
||||
c.withNode n, result, kind:
|
||||
if n.kind == FloatLit:
|
||||
result.floatVal = pool.floats[n.floatId]
|
||||
inc n
|
||||
elif n.kind == ParLe:
|
||||
case pool.tags[n.tagId]
|
||||
of "inf":
|
||||
result.floatVal = Inf
|
||||
of "nan":
|
||||
result.floatVal = NaN
|
||||
of "neginf":
|
||||
result.floatVal = NegInf
|
||||
else:
|
||||
raiseAssert "expected float literal but got " & pool.tags[n.tagId]
|
||||
inc n
|
||||
skipParRi n
|
||||
else:
|
||||
raiseAssert "expected float literal but got " & $n.kind
|
||||
of nkStrLit .. nkTripleStrLit:
|
||||
c.withNode n, result, kind:
|
||||
expect n, StringLit
|
||||
result.strVal = pool.strings[n.litId]
|
||||
inc n
|
||||
of nkNilLit:
|
||||
c.withNode n, result, kind:
|
||||
discard
|
||||
else:
|
||||
c.withNode n, result, kind:
|
||||
while n.kind != ParRi:
|
||||
result.sons.add c.loadNode(n)
|
||||
else:
|
||||
raiseAssert "Not yet implemented " & $n.kind
|
||||
|
||||
when false:
|
||||
proc loadNifModule*(c: var DecodeContext; f: FileIndex): PNode =
|
||||
let moduleSuffix = moduleSuffix(c.infos.config, f)
|
||||
let modFile = toGeneratedFile(c.infos.config, AbsoluteFile(moduleSuffix), ".nif").string
|
||||
|
||||
var buf = createTokenBuf(300)
|
||||
var s = nifstreams.open(modFile)
|
||||
# XXX We can optimize this here and only load the top level entries!
|
||||
try:
|
||||
nifcursors.parse(s, buf, NoLineInfo)
|
||||
finally:
|
||||
nifstreams.close(s)
|
||||
var n = cursorAt(buf, 0)
|
||||
result = loadNode(c, n)
|
||||
|
||||
when isMainModule:
|
||||
import std / syncio
|
||||
let obj = parseSymName("a.123.sys")
|
||||
echo obj.name, " ", obj.module, " ", obj.count
|
||||
let objb = parseSymName("abcdef.0121")
|
||||
echo objb.name, " ", objb.module, " ", objb.count
|
||||
@@ -713,6 +713,70 @@ iterator items*(tab: TStrTable): PSym =
|
||||
yield s
|
||||
s = nextIter(it, tab)
|
||||
|
||||
proc isNil(x: ItemId): bool {.inline.} =
|
||||
x.module == 0 and x.item == 0
|
||||
|
||||
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
|
||||
for h in 0..high(data):
|
||||
if isNil(data[h].key):
|
||||
return true
|
||||
result = false
|
||||
|
||||
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while not isNil(t.data[h].key):
|
||||
if toId(t.data[h].key) == key:
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
|
||||
var index = idTableRawGet(t, toId(key))
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = default(T)
|
||||
|
||||
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
|
||||
getOrDefault(t, key.itemId)
|
||||
|
||||
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
|
||||
var h: Hash
|
||||
let keyId = toId(key)
|
||||
h = keyId and high(data)
|
||||
while not isNil(data[h].key):
|
||||
assert(toId(data[h].key) != keyId)
|
||||
h = nextTry(h, high(data))
|
||||
assert(isNil(data[h].key))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
|
||||
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
|
||||
var
|
||||
index: int
|
||||
n: TIdPairSeq[T]
|
||||
index = idTableRawGet(t, toId(key))
|
||||
if index >= 0:
|
||||
assert(not isNil(t.data[index].key))
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(t.data.len, t.counter):
|
||||
newSeq(n, t.data.len * GrowthFactor)
|
||||
for i in 0..high(t.data):
|
||||
if not isNil(t.data[i].key):
|
||||
idTableRawInsert(n, t.data[i].key, t.data[i].val)
|
||||
assert(hasEmptySlot(n))
|
||||
swap(t.data, n)
|
||||
idTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
|
||||
t[key.itemId] = val
|
||||
|
||||
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
|
||||
for i in 0..high(t.data):
|
||||
if not isNil(t.data[i].key):
|
||||
yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc initIITable(x: var TIITable) =
|
||||
x.counter = 0
|
||||
newSeq(x.data, StartSize)
|
||||
|
||||
1033
compiler/astdef.nim
Normal file
1033
compiler/astdef.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,121 +0,0 @@
|
||||
type
|
||||
Snippet = string
|
||||
Builder = string
|
||||
|
||||
template newBuilder(s: string): Builder =
|
||||
s
|
||||
|
||||
proc addField(obj: var Builder; name, typ: Snippet) =
|
||||
obj.add('\t')
|
||||
obj.add(typ)
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
obj.add(";\n")
|
||||
|
||||
proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool; initializer: Snippet) =
|
||||
obj.add('\t')
|
||||
if field.alignment > 0:
|
||||
obj.add("NIM_ALIGN(")
|
||||
obj.addInt(field.alignment)
|
||||
obj.add(") ")
|
||||
obj.add(typ)
|
||||
if sfNoalias in field.flags:
|
||||
obj.add(" NIM_NOALIAS")
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
if isFlexArray:
|
||||
obj.add("[SEQ_DECL_SIZE]")
|
||||
if field.bitsize != 0:
|
||||
obj.add(":")
|
||||
obj.addInt(field.bitsize)
|
||||
if initializer.len != 0:
|
||||
obj.add(initializer)
|
||||
obj.add(";\n")
|
||||
|
||||
proc structOrUnion(t: PType): Snippet =
|
||||
let t = t.skipTypes({tyAlias, tySink})
|
||||
if tfUnion in t.flags: "union"
|
||||
else: "struct"
|
||||
|
||||
proc ptrType(t: Snippet): Snippet =
|
||||
t & "*"
|
||||
|
||||
template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: string; body: typed) =
|
||||
if tfPacked in typ.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" __attribute__((__packed__))")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\n")
|
||||
obj.add(structOrUnion(typ))
|
||||
else:
|
||||
obj.add(structOrUnion(typ))
|
||||
obj.add(" ")
|
||||
obj.add(name)
|
||||
type BaseClassKind = enum
|
||||
bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
|
||||
var baseKind = bcNone
|
||||
if typ.kind == tyObject:
|
||||
if typ.baseClass == nil:
|
||||
if lacksMTypeField(typ):
|
||||
baseKind = bcNone
|
||||
elif optTinyRtti in m.config.globalOptions:
|
||||
baseKind = bcNoneTinyRtti
|
||||
else:
|
||||
baseKind = bcNoneRtti
|
||||
elif m.compileToCpp:
|
||||
baseKind = bcCppInherit
|
||||
else:
|
||||
baseKind = bcSupField
|
||||
if baseKind == bcCppInherit:
|
||||
obj.add(" : public ")
|
||||
obj.add(baseType)
|
||||
obj.add(" ")
|
||||
obj.add("{\n")
|
||||
let currLen = obj.len
|
||||
case baseKind
|
||||
of bcNone:
|
||||
# rest of the options add a field or don't need it due to inheritance,
|
||||
# we need to add the dummy field for uncheckedarray ahead of time
|
||||
# so that it remains trailing
|
||||
if typ.itemId notin m.g.graph.memberProcsPerType and
|
||||
typ.n != nil and typ.n.len == 1 and typ.n[0].kind == nkSym and
|
||||
typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
|
||||
# only consists of flexible array field, add *initial* dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
of bcCppInherit: discard
|
||||
of bcNoneRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
|
||||
of bcNoneTinyRtti:
|
||||
obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
|
||||
of bcSupField:
|
||||
obj.addField(name = "Sup", typ = baseType)
|
||||
body
|
||||
if baseKind == bcNone and currLen == obj.len and typ.itemId notin m.g.graph.memberProcsPerType:
|
||||
# no fields were added, add dummy field
|
||||
obj.addField(name = "dummy", typ = "char")
|
||||
obj.add("};\n")
|
||||
if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
|
||||
## adds a field with a `struct { ... }` type, building it according to `body`
|
||||
obj.add('\t')
|
||||
if tfPacked in parentTyp.flags:
|
||||
if hasAttribute in CC[m.config.cCompiler].props:
|
||||
obj.add("struct __attribute__((__packed__)) {\n")
|
||||
else:
|
||||
obj.add("#pragma pack(push, 1)\nstruct {")
|
||||
else:
|
||||
obj.add("struct {\n")
|
||||
body
|
||||
obj.add("} ")
|
||||
obj.add(fieldName)
|
||||
obj.add(";\n")
|
||||
if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
|
||||
result.add("#pragma pack(pop)\n")
|
||||
|
||||
template addAnonUnion(obj: var Builder; body: typed) =
|
||||
obj.add "union{\n"
|
||||
body
|
||||
obj.add("};\n")
|
||||
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 and p.config.exc != excGoto:
|
||||
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))
|
||||
@@ -307,15 +338,16 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
|
||||
else:
|
||||
result = a
|
||||
|
||||
proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
proc expressionsNeedsTmp(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)
|
||||
@@ -326,7 +358,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
(optByRef notin param.options or not p.module.compileToCpp):
|
||||
a = initLocExpr(p, n)
|
||||
if n.kind in {nkCharLit..nkNilLit}:
|
||||
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
|
||||
addAddrLoc(p.config, expressionsNeedsTmp(p, a), result)
|
||||
else:
|
||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
@@ -337,7 +369,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
|
||||
if needsIndirect:
|
||||
n.typ() = n.typ.exactReplica
|
||||
n.typ.flags.incl tfVarIsPtr
|
||||
n.typ.incl tfVarIsPtr
|
||||
a = initLocExprSingleUse(p, n)
|
||||
a = withTmpIfNeeded(p, a, needsTmp)
|
||||
if needsIndirect: a.flags.incl lfIndirect
|
||||
@@ -356,12 +388,11 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
|
||||
a.snippet = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
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)
|
||||
@@ -387,35 +418,6 @@ proc skipTrivialIndirections(n: PNode): PNode =
|
||||
result = result[1]
|
||||
else: break
|
||||
|
||||
proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
|
||||
case n.kind:
|
||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
||||
of nkSym:
|
||||
if mutate: result.add n
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
getPotentialWrites(n[0], true, result)
|
||||
getPotentialWrites(n[1], mutate, result)
|
||||
of nkAddr, nkHiddenAddr:
|
||||
getPotentialWrites(n[0], true, result)
|
||||
of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr:
|
||||
getPotentialWrites(n[0], mutate, result)
|
||||
of nkCallKinds:
|
||||
case n.getMagic:
|
||||
of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
|
||||
getPotentialWrites(n[1], true, result)
|
||||
for i in 2..<n.len:
|
||||
getPotentialWrites(n[i], mutate, result)
|
||||
of mSwap:
|
||||
for i in 1..<n.len:
|
||||
getPotentialWrites(n[i], true, result)
|
||||
else:
|
||||
for i in 1..<n.len:
|
||||
getPotentialWrites(n[i], mutate, result)
|
||||
else:
|
||||
for s in n:
|
||||
getPotentialWrites(s, mutate, result)
|
||||
|
||||
proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
||||
case n.kind:
|
||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
||||
@@ -424,7 +426,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 +448,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 +477,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 +517,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 +542,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 +557,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 +582,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 +592,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 +659,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 +694,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 +712,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 +730,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 +757,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 +767,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 +834,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,40 +61,51 @@ 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)
|
||||
specializeResetT(p, accessor.parentObj(p.module), x)
|
||||
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
|
||||
if typ.n != nil:
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
# imported C struct, nimZeroMem
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
||||
cCast(ptrType(CPointer), cAddr(accessor)),
|
||||
cSizeof(getTypeDesc(p.module, typ)))
|
||||
else:
|
||||
specializeResetN(p, accessor, typ.n, typ)
|
||||
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,
|
||||
|
||||
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
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, types, msgs, wordrecg,
|
||||
platform, trees, options, cgendata, mangleutils
|
||||
platform, trees, options, cgendata, mangleutils, renderer
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
@@ -90,9 +90,6 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
|
||||
# forwarded objects are *always* passed by pointers for consistency!
|
||||
result = true
|
||||
elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
|
||||
# bug #23354:
|
||||
result = false
|
||||
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
|
||||
result = true # requested anyway
|
||||
elif (tfFinal in pt.flags) and (pt[0] == nil):
|
||||
@@ -120,14 +117,14 @@ proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string =
|
||||
#Module::Type
|
||||
var name = s.name.s
|
||||
var name = s.name.s & extra
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
proc encodeType*(m: BModule; t: PType; staticLists: var string): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
@@ -138,10 +135,10 @@ proc encodeType*(m: BModule; t: PType): string =
|
||||
result = encodeName(t[0].sym.name.s)
|
||||
result.add "I"
|
||||
for i in 1..<t.len - 1:
|
||||
result.add encodeType(m, t[i])
|
||||
result.add encodeType(m, t[i], staticLists)
|
||||
result.add "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
@@ -150,8 +147,13 @@ proc encodeType*(m: BModule; t: PType): string =
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s)
|
||||
result.add encodeType(m, s, staticLists)
|
||||
result.add "E"
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
staticLists.add "_s" & renderTree(t.n)
|
||||
else:
|
||||
raiseAssert "unreachable"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
@@ -164,7 +166,7 @@ proc encodeType*(m: BModule; t: PType): string =
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType)
|
||||
result = encodeType(m, t.elementType, staticLists)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
|
||||
1545
compiler/cgen.nim
1545
compiler/cgen.nim
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, ropes, options,
|
||||
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
|
||||
@@ -115,11 +115,11 @@ 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
|
||||
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
||||
forwardedProcs*: seq[PSym] # procs that did not yet have a body
|
||||
generatedHeader*: BModule
|
||||
typeInfoMarker*: TypeCacheWithOwner
|
||||
typeInfoMarkerV2*: TypeCacheWithOwner
|
||||
@@ -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
|
||||
@@ -155,20 +155,22 @@ type
|
||||
forwTypeCache*: TypeCache # cache for forward declarations of types
|
||||
declaredThings*: IntSet # things we have declared in this .c file
|
||||
declaredProtos*: IntSet # prototypes we have declared in this .c file
|
||||
queue*: seq[PSym] # queue of procs to generate
|
||||
alive*: IntSet # symbol IDs of alive data as computed by `dce.nim`
|
||||
headerFiles*: seq[string] # needed headers to include
|
||||
typeInfoMarker*: TypeCache # needed for generating type information
|
||||
typeInfoMarkerV2*: TypeCache
|
||||
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
|
||||
@@ -177,22 +179,25 @@ template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
|
||||
|
||||
proc delayedCodegen*(m: BModule): bool {.inline.} =
|
||||
useAliveDataFromDce in m.flags or m.config.globalOptions.contains(optCompress)
|
||||
|
||||
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(
|
||||
|
||||
@@ -123,8 +123,8 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||
|
||||
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
var disp = copySym(s, idgen)
|
||||
incl(disp.flags, sfDispatcher)
|
||||
excl(disp.flags, sfExported)
|
||||
incl(disp, sfDispatcher)
|
||||
excl(disp, sfExported)
|
||||
let old = disp.typ
|
||||
disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
|
||||
copyTypeProps(g, idgen.module, disp.typ, old)
|
||||
@@ -133,7 +133,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
||||
disp.ast = copyTree(s.ast)
|
||||
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||
disp.loc.snippet = ""
|
||||
disp.locImpl.snippet = ""
|
||||
if s.typ.returnType != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -364,6 +364,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
result = false
|
||||
of "panics": result = contains(conf.globalOptions, optPanics)
|
||||
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
|
||||
of "mangle": result = contains(conf.globalOptions, optItaniumMangle)
|
||||
else:
|
||||
result = false
|
||||
invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
@@ -459,7 +460,7 @@ template handleStdinOrCmdInput =
|
||||
conf.outDir = getNimcacheDir(conf)
|
||||
|
||||
proc handleStdinInput*(conf: ConfigRef) =
|
||||
conf.projectName = "stdinfile"
|
||||
conf.projectName = conf.stdinFile.string
|
||||
conf.projectIsStdin = true
|
||||
handleStdinOrCmdInput()
|
||||
|
||||
@@ -473,6 +474,7 @@ proc parseCommand*(command: string): Command =
|
||||
of "cpp", "compiletocpp": cmdCompileToCpp
|
||||
of "objc", "compiletooc": cmdCompileToOC
|
||||
of "js", "compiletojs": cmdCompileToJS
|
||||
of "nif": cmdCompileToNif
|
||||
of "r": cmdCrun
|
||||
of "m": cmdM
|
||||
of "run": cmdTcc
|
||||
@@ -507,6 +509,7 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
of cmdCompileToCpp: conf.backend = backendCpp
|
||||
of cmdCompileToOC: conf.backend = backendObjc
|
||||
of cmdCompileToJS: conf.backend = backendJs
|
||||
of cmdCompileToNif: conf.backend = backendNif
|
||||
else: discard
|
||||
|
||||
proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||
@@ -760,6 +763,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf.globalOptions.excl optCDebug
|
||||
else:
|
||||
localError(conf, info, "expected native|gdb|on|off but found " & arg)
|
||||
of "mangle":
|
||||
case arg.normalize
|
||||
of "nim":
|
||||
conf.globalOptions.excl optItaniumMangle
|
||||
of "cpp":
|
||||
conf.globalOptions.incl optItaniumMangle
|
||||
else:
|
||||
localError(conf, info, "expected nim|cpp but found " & arg)
|
||||
of "compress":
|
||||
conf.globalOptions.incl optCompress
|
||||
of "g": # alias for --debugger:native
|
||||
conf.globalOptions.incl optCDebug
|
||||
conf.options.incl optLineDir
|
||||
@@ -882,11 +895,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "import":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitImports.add m
|
||||
of "include":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitIncludes.add m
|
||||
of "listcmd":
|
||||
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||
of "asm":
|
||||
@@ -935,6 +956,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
var value: int = 0
|
||||
discard parseSaturatedNatural(arg, value)
|
||||
conf.errorMax = if value == 0: high(int) else: value
|
||||
of "stdinfile":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.stdinFile = if os.isAbsolute(arg): AbsoluteFile(arg)
|
||||
else: AbsoluteFile(getCurrentDir() / arg)
|
||||
of "verbosity":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
let verbosity = parseInt(arg)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
|
||||
import std/intsets
|
||||
|
||||
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
const
|
||||
logBindings = false
|
||||
logBindings = when defined(debugConcepts): true else: false
|
||||
|
||||
## Code dealing with Concept declarations
|
||||
## --------------------------------------
|
||||
@@ -29,7 +29,7 @@ proc declareSelf(c: PContext; info: TLineInfo) =
|
||||
let ow = getCurrOwner(c)
|
||||
let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
|
||||
s.typ = newType(tyTypeDesc, c.idgen, ow)
|
||||
s.typ.flags.incl {tfUnresolved, tfPacked}
|
||||
s.typ.incl {tfUnresolved, tfPacked}
|
||||
s.typ.add newType(tyEmpty, c.idgen, ow)
|
||||
addDecl(c, s, info)
|
||||
|
||||
@@ -70,26 +70,87 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
||||
## ----------------
|
||||
|
||||
type
|
||||
MatchFlags* = enum
|
||||
mfDontBind # Do not bind generic parameters
|
||||
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
|
||||
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
|
||||
## that turned out to be wrong.
|
||||
bindings: LayeredIdTable
|
||||
marker: IntSet ## Some protection against wild runaway recursions.
|
||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType
|
||||
concpt: PType ## current concept being evaluated
|
||||
depthCount = 0
|
||||
flags: set[MatchFlags]
|
||||
|
||||
MatchKind = enum
|
||||
mkNoMatch, mkSubset, mkSame
|
||||
|
||||
const
|
||||
asymmetricConceptParamMods = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred} # param modifiers that to not have to match implementation -> concept
|
||||
bindableTypes = {tyGenericParam, tyOr, tyTypeDesc}
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool
|
||||
|
||||
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool
|
||||
|
||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
||||
## checks if we bound the type variable 'key' already to some
|
||||
## concrete type.
|
||||
for i in 0..<m.inferred.len:
|
||||
if m.inferred[i][0] == key: return m.inferred[i][1]
|
||||
return nil
|
||||
result = m.bindings.lookup(key)
|
||||
if result == nil:
|
||||
result = key
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyAlias, tyInferred}
|
||||
|
||||
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool
|
||||
proc unrollGenericParam(param: PType): PType =
|
||||
result = param.skipTypes(ignorableForArgType)
|
||||
while result.kind in {tyGenericParam, tyTypeDesc} and result.hasElementType and result.elementType.kind != tyNone:
|
||||
result = result.elementType
|
||||
|
||||
proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable .} =
|
||||
if v.kind == tyTypeDesc:
|
||||
return false
|
||||
var value = unrollGenericParam(v)
|
||||
if value.kind == tyGenericParam:
|
||||
value = existingBinding(m, value)
|
||||
if value.kind == tyGenericParam:
|
||||
if value.hasElementType:
|
||||
value = value.elementType
|
||||
else:
|
||||
return true
|
||||
if value.kind == tyStatic:
|
||||
return false
|
||||
|
||||
if m.magic in {mArrPut, mArrGet} and value.kind in arrPutGetMagicApplies:
|
||||
value = value.last
|
||||
|
||||
let old = existingBinding(m, key)
|
||||
if old != key:
|
||||
# check previously bound value
|
||||
if not matchType(c, old, value, m):
|
||||
return false
|
||||
elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone:
|
||||
# check constaint
|
||||
if matchType(c, unrollGenericParam(key), value, m) == false:
|
||||
return false
|
||||
|
||||
when logBindings: echo "bind table adding '", key, "', ", value
|
||||
assert value != nil
|
||||
assert value.kind != tyVoid
|
||||
m.bindings.put(key, value)
|
||||
return true
|
||||
|
||||
proc defSignatureType(n: PNode): PType = n[0].sym.typ
|
||||
|
||||
proc conceptBody*(n: PType): PNode = n.n.lastSon
|
||||
|
||||
proc acceptsAllTypes(t: PType): bool=
|
||||
result = false
|
||||
@@ -101,84 +162,155 @@ proc acceptsAllTypes(t: PType): bool=
|
||||
if not t.hasElementType or t.elementType.kind == tyNone:
|
||||
result = true
|
||||
|
||||
proc procDefSignature(s: PSym): PNode {. deprecated .} =
|
||||
var nc = s.ast.copyNode()
|
||||
for i in 0 .. 5:
|
||||
nc.add s.ast[i]
|
||||
nc
|
||||
|
||||
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):
|
||||
for i in start ..< f.kidsLen - ord(f.kind in {tyGenericInst, tyGenericInvocation}):
|
||||
if not matchType(c, f[i], a[i], m): return false
|
||||
|
||||
proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
iterator traverseTyOr(t: PType): PType {. closure .}=
|
||||
for i in t.kids:
|
||||
case i.kind:
|
||||
of tyGenericParam:
|
||||
if i.hasElementType:
|
||||
for s in traverseTyOr(i.elementType):
|
||||
yield s
|
||||
else:
|
||||
yield i
|
||||
else:
|
||||
yield i
|
||||
|
||||
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
|
||||
assert not(potentialImpl.reduceToBase.kind == tyConcept)
|
||||
let concpt = f.reduceToBase
|
||||
if m.depthCount > 0:
|
||||
# concepts that are more then 2 levels deep are treated like
|
||||
# tyAnything to stop dependencies from getting out of control
|
||||
return true
|
||||
var efPot = potentialImpl
|
||||
if potentialImpl.isSelf:
|
||||
if m.concpt.n == concpt.n:
|
||||
return true
|
||||
efPot = m.potentialImplementation
|
||||
|
||||
var oldBindings = m.bindings
|
||||
m.bindings = newTypeMapLayer(m.bindings)
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = efPot
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = concpt
|
||||
|
||||
var invocation: PType = nil
|
||||
if f.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
invocation = f
|
||||
inc m.depthCount
|
||||
result = processConcept(c, concpt, invocation, oldBindings, m)
|
||||
dec m.depthCount
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.bindings = oldBindings
|
||||
|
||||
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
|
||||
if fn.kind != an.kind:
|
||||
return false
|
||||
if fn[namePos].sym.name != an[namePos].sym.name:
|
||||
return false
|
||||
let
|
||||
ft = fn.defSignatureType
|
||||
at = an.defSignatureType
|
||||
if ft.len != at.len:
|
||||
return false
|
||||
|
||||
for i in 1 ..< ft.n.len:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
|
||||
let aType = at.n[i].typ
|
||||
let fType = ft.n[i].typ
|
||||
|
||||
if aType.isSelf and fType.isSelf:
|
||||
continue
|
||||
|
||||
if not matchType(c, fType, aType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
result = true
|
||||
if not matchReturnType(c, ft.returnType, at.returnType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
result = false
|
||||
|
||||
proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
|
||||
# XXX: In the future this may need extra parameters to carry info for container types
|
||||
if fc.n == ac.n:
|
||||
# This will have to take generic parameters into account at some point
|
||||
return mkSame
|
||||
let
|
||||
fn = fc.conceptBody
|
||||
an = ac.conceptBody
|
||||
sameLen = fc.len == ac.len
|
||||
var match = false
|
||||
for fdef in fn:
|
||||
var cmpResult = false
|
||||
for ia, ndef in an:
|
||||
match = cmpConceptDefs(c, fdef, ndef, m)
|
||||
if match:
|
||||
break
|
||||
if not match:
|
||||
return mkNoMatch
|
||||
return mkSubset
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
## of a routine that might match.
|
||||
const
|
||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
||||
|
||||
var a = ao
|
||||
var
|
||||
a = ao
|
||||
f = fo
|
||||
if a.isSelf:
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
return false
|
||||
a = m.potentialImplementation
|
||||
if a.kind in bindableTypes:
|
||||
a = existingBinding(m, ao)
|
||||
if a == ao and a.kind == tyGenericParam and a.hasElementType and a.elementType.kind != tyNone:
|
||||
a = a.elementType
|
||||
|
||||
case a.kind
|
||||
of tyGenericParam:
|
||||
let binding = m.existingBinding(a)
|
||||
if binding != nil:
|
||||
a = binding
|
||||
else:
|
||||
discard
|
||||
if f.isConcept:
|
||||
if a.acceptsAllTypes:
|
||||
return false
|
||||
if a.skipTypes(ignorableForArgType).isConcept:
|
||||
# if f is a subset of a then any match to a will also match f. Not the other way around
|
||||
return conceptsMatch(c, a.reduceToBase, f.reduceToBase, m) >= mkSubset
|
||||
else:
|
||||
return matchConceptToImpl(c, f, a, m)
|
||||
|
||||
result = false
|
||||
|
||||
case f.kind
|
||||
of tyAlias:
|
||||
result = matchType(c, f.skipModifier, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
let ua = a.skipTypes(asymmetricConceptParamMods)
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
result = false
|
||||
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
|
||||
m.inferred.add((a, last m.potentialImplementation))
|
||||
bindParam(c, m, a, last m.potentialImplementation)
|
||||
result = true
|
||||
#elif ua.isConcept:
|
||||
# result = matchType(c, m.concpt, ua, m)
|
||||
else:
|
||||
result = matchType(c, a, m.potentialImplementation, m)
|
||||
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
|
||||
else:
|
||||
if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
|
||||
if f.hasElementType:
|
||||
if a.kind == tyTypeDesc:
|
||||
if not(a.hasElementType) or a.elementType.kind == tyNone:
|
||||
result = true
|
||||
elif f.hasElementType:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
result = true # both lack it
|
||||
else:
|
||||
result = false
|
||||
of tyGenericInvocation:
|
||||
result = false
|
||||
if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
|
||||
if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
|
||||
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):
|
||||
result = false
|
||||
else:
|
||||
let old = existingBinding(m, f)
|
||||
if old == nil:
|
||||
if f.hasElementType and f.elementType.kind != tyNone:
|
||||
# also check the generic's constraints:
|
||||
let oldLen = m.inferred.len
|
||||
result = matchType(c, f.elementType, a, m)
|
||||
m.inferred.setLen oldLen
|
||||
if result:
|
||||
when logBindings: echo "A adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
|
||||
when logBindings: echo "B adding ", f, " ", last ak
|
||||
m.inferred.add((f, last ak))
|
||||
result = true
|
||||
else:
|
||||
when logBindings: echo "C adding ", f, " ", ak
|
||||
m.inferred.add((f, ak))
|
||||
#echo "binding ", typeToString(ak), " to ", typeToString(f)
|
||||
result = true
|
||||
elif not m.marker.containsOrIncl(old.id):
|
||||
result = matchType(c, old, ak, m)
|
||||
if m.magic == mArrPut and ak.kind == tyGenericParam:
|
||||
result = true
|
||||
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.
|
||||
@@ -186,91 +318,57 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
elif m.magic == mArrPut:
|
||||
result = matchType(c, f.elementType, a, m)
|
||||
else:
|
||||
result = false
|
||||
of tyEnum, tyObject, tyDistinct:
|
||||
result = sameType(f, a)
|
||||
if a.kind in ignorableForArgType:
|
||||
result = matchType(c, f, a.skipTypes(ignorableForArgType), m)
|
||||
else:
|
||||
if a.kind == tyGenericInst:
|
||||
# tyOr does this to generic typeclasses
|
||||
result = a.base.sym == f.sym
|
||||
else:
|
||||
result = sameType(f, a)
|
||||
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
|
||||
result = a.skipTypes(ignorableForArgType).kind == f.kind
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
let ak = a.skipTypes(ignorableForArgType)
|
||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyConcept:
|
||||
if a.kind == tyConcept and f.n == a.n:
|
||||
result = true
|
||||
elif m.concpt.size == szIllegalRecursion:
|
||||
result = false
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = a
|
||||
m.concpt.size = szIllegalRecursion
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = f
|
||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.concpt.size = szUnknownSize
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
of tyGenericBody:
|
||||
var ak = a
|
||||
if a.kind == tyGenericBody:
|
||||
ak = last(a)
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyCompositeTypeClass:
|
||||
var ak = if a.kind == tyCompositeTypeClass: a.last else: a
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
||||
tyGenericInst:
|
||||
# ^ XXX Rewrite this logic, it's more complex than it needs to be.
|
||||
if f.kind == tyArray and f.kidsLen == 3:
|
||||
(ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
|
||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr:
|
||||
if f.kind == tyArray and f.kidsLen == 3 and a.kind == tyArray:
|
||||
# XXX: this is a work-around!
|
||||
# system.nim creates these for the magic array typeclass
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
||||
if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
|
||||
result = matchKids(c, f, ak, m)
|
||||
of tyOr:
|
||||
let oldLen = m.inferred.len
|
||||
if a.kind == tyOr:
|
||||
# say the concept requires 'int|float|string' if the potentialImplementation
|
||||
# says 'int|string' that is good enough.
|
||||
var covered = 0
|
||||
for ff in f.kids:
|
||||
for aa in a.kids:
|
||||
let oldLenB = m.inferred.len
|
||||
let r = matchType(c, ff, aa, m)
|
||||
if r:
|
||||
inc covered
|
||||
if ak.kind == f.kind:
|
||||
if f.base.kind == tyNone:
|
||||
result = true
|
||||
elif f.kidsLen == ak.kidsLen:
|
||||
result = matchKids(c, f, ak, m)
|
||||
of tyGenericInvocation, tyGenericInst:
|
||||
result = false
|
||||
let ea = a.skipTypes(ignorableForArgType)
|
||||
if ea.kind in {tyGenericInst, tyGenericInvocation}:
|
||||
var
|
||||
k1 = f.kidsLen - ord(f.kind == tyGenericInst)
|
||||
k2 = ea.kidsLen - ord(ea.kind == tyGenericInst)
|
||||
if sameType(f.genericHead, ea.genericHead) and k1 == k2:
|
||||
result = true
|
||||
for i in 1 ..< k2:
|
||||
if not matchType(c, f[i], ea[i], m):
|
||||
result = false
|
||||
break
|
||||
m.inferred.setLen oldLenB
|
||||
|
||||
result = covered >= a.kidsLen
|
||||
if not result:
|
||||
m.inferred.setLen oldLen
|
||||
else:
|
||||
result = false
|
||||
for ff in f.kids:
|
||||
result = matchType(c, ff, a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.inferred.setLen oldLen
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
let oldLen = m.inferred.len
|
||||
result = not matchType(c, f.elementType, a, m)
|
||||
m.inferred.setLen oldLen
|
||||
of tyAnything:
|
||||
result = true
|
||||
elif f.kind == tyGenericInvocation:
|
||||
# bind potential generic constraints into body
|
||||
let body = f.base
|
||||
for i in 1 ..< len(f):
|
||||
bindParam(c,m,body[i-1], f[i])
|
||||
result = matchType(c, body, a, m)
|
||||
else: # tyGenericInst
|
||||
result = matchType(c, f.last, a, m)
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
of tyStatic:
|
||||
result = false
|
||||
var scomp = f.base
|
||||
if scomp.kind == tyGenericParam:
|
||||
if f.base.kidsLen > 0:
|
||||
@@ -279,8 +377,75 @@ proc matchType(c: PContext; f, ao: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, scomp, a.base, m)
|
||||
else:
|
||||
result = matchType(c, scomp, a, m)
|
||||
of tyGenericParam:
|
||||
if a.acceptsAllTypes:
|
||||
discard bindParam(c, m, f, a)
|
||||
result = f.acceptsAllTypes
|
||||
else:
|
||||
result = bindParam(c, m, f, a)
|
||||
of tyAnything:
|
||||
result = true
|
||||
of tyNot:
|
||||
if a.kind == tyNot:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = not matchType(c, f.elementType, a, m)
|
||||
m.bindings.setToPreviousLayer()
|
||||
of tyAnd:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = true
|
||||
for ff in traverseTyOr(f):
|
||||
let r = matchType(c, ff, a, m)
|
||||
if not r:
|
||||
m.bindings.setToPreviousLayer()
|
||||
result = false
|
||||
break
|
||||
of tyGenericBody:
|
||||
var ak = a
|
||||
if a.kind == tyGenericBody:
|
||||
ak = last(a)
|
||||
result = matchType(c, last(f), ak, m)
|
||||
of tyCompositeTypeClass:
|
||||
if a.kind == tyCompositeTypeClass:
|
||||
result = matchKids(c, f, a, m)
|
||||
else:
|
||||
result = matchType(c, last(f), a, m)
|
||||
of tyBuiltInTypeClass:
|
||||
let target = f.genericHead.kind
|
||||
result = a.skipTypes(ignorableForArgType).reduceToBase.kind == target
|
||||
of tyOr:
|
||||
if a.kind == tyOr:
|
||||
var covered = 0
|
||||
for ff in traverseTyOr(f):
|
||||
for aa in traverseTyOr(a):
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
let r = matchType(c, ff, aa, m)
|
||||
if r:
|
||||
inc covered
|
||||
break
|
||||
m.bindings.setToPreviousLayer()
|
||||
|
||||
result = covered >= a.kidsLen
|
||||
else:
|
||||
for ff in f.kids:
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
result = matchType(c, ff, a, m)
|
||||
if result: break # and remember the binding!
|
||||
m.bindings.setToPreviousLayer()
|
||||
of tySet:
|
||||
result = false
|
||||
if a.kind == tySet:
|
||||
result = matchType(c, f.elementType, a.elementType, m)
|
||||
else:
|
||||
result = false
|
||||
if result and ao.kind == tyGenericParam:
|
||||
let bf = if f.isSelf: m.potentialImplementation else: f
|
||||
if bindParam(c, m, ao, bf):
|
||||
when logBindings: echo " ^ reverse binding"
|
||||
|
||||
proc checkConstraint(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f, a, m) or matchType(c, a, f, m)
|
||||
|
||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
## Like 'matchType' but with extra logic dealing with proc return types
|
||||
@@ -290,30 +455,38 @@ proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
||||
elif a == nil:
|
||||
result = false
|
||||
else:
|
||||
result = matchType(c, f, a, m)
|
||||
result = checkConstraint(c, f, a, m)
|
||||
|
||||
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
|
||||
## or similar.
|
||||
|
||||
# watch out: only add bindings after a completely successful match.
|
||||
let oldLen = m.inferred.len
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
|
||||
let can = candidate.typ.n
|
||||
let con = n[0].sym.typ.n
|
||||
|
||||
let con = defSignatureType(n).n
|
||||
if can.len < con.len:
|
||||
# too few arguments, cannot be a match:
|
||||
return false
|
||||
|
||||
|
||||
if can.len > con.len:
|
||||
# too many arguments (not optional)
|
||||
for i in con.len ..< can.len:
|
||||
if can[i].sym.ast == nil:
|
||||
return false
|
||||
|
||||
when defined(debugConcepts):
|
||||
echo "considering: ", renderTree(candidate.procDefSignature), " ", candidate.magic
|
||||
|
||||
let common = min(can.len, con.len)
|
||||
for i in 1 ..< common:
|
||||
if not matchType(c, con[i].typ, can[i].typ, m):
|
||||
m.inferred.setLen oldLen
|
||||
if not checkConstraint(c, con[i].typ, can[i].typ, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
|
||||
if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
|
||||
m.inferred.setLen oldLen
|
||||
|
||||
if not matchReturnType(c, n.defSignatureType.returnType, candidate.typ.returnType, m):
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
|
||||
# all other parameters have to be optional parameters:
|
||||
@@ -321,7 +494,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
assert can[i].kind == nkSym
|
||||
if can[i].sym.ast == nil:
|
||||
# has too many arguments one of which is not optional:
|
||||
m.inferred.setLen oldLen
|
||||
m.bindings.setToPreviousLayer()
|
||||
return false
|
||||
|
||||
return true
|
||||
@@ -329,13 +502,14 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
||||
result = false
|
||||
var candidates = searchScopes(c, n[namePos].sym.name, kinds)
|
||||
searchImportsAll(c, n[namePos].sym.name, kinds, candidates)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
if matchSym(c, candidate, n, m): return true
|
||||
result = false
|
||||
if matchSym(c, candidate, n, m):
|
||||
result = true
|
||||
break
|
||||
|
||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
|
||||
@@ -368,7 +542,48 @@ 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 LayeredIdTable; invocation: PType): bool =
|
||||
proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; m: var MatchCon) =
|
||||
# invocation != nil means we have a non-atomic concept:
|
||||
if invocation != nil and invocation.kind == tyGenericInvocation:
|
||||
assert concpt.sym.typ.kind == tyGenericBody
|
||||
|
||||
for i in 0 .. concpt.sym.typ.len - 1:
|
||||
let thisSym = concpt.sym.typ[i]
|
||||
if lookup(bindings, thisSym) != nil:
|
||||
# dont trust the bindings over existing ones
|
||||
continue
|
||||
let found = m.bindings.lookup(thisSym)
|
||||
if found != nil:
|
||||
when logBindings: echo "Invocation bind: ", thisSym, " ", found
|
||||
bindings.put(thisSym, found)
|
||||
|
||||
# bind even more generic parameters
|
||||
let genBody = invocation.base
|
||||
assert genBody.kind == tyGenericBody
|
||||
for i in FirstGenericParamAt ..< invocation.kidsLen:
|
||||
let bpram = genBody[i - 1]
|
||||
if lookup(bindings, invocation[i]) != nil:
|
||||
# dont trust the bindings over existing ones
|
||||
continue
|
||||
let boundV = lookup(bindings, bpram)
|
||||
when logBindings: echo "generic body bind: '", invocation[i], "' '", boundV, "'"
|
||||
if boundV != nil:
|
||||
bindings.put(invocation[i], boundV)
|
||||
bindings.put(concpt, m.potentialImplementation)
|
||||
|
||||
proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool =
|
||||
m.bindings = m.bindings.newTypeMapLayer()
|
||||
if invocation != nil and invocation.kind == tyGenericInst:
|
||||
let genericBody = invocation.base
|
||||
for i in 1..<invocation.kidsLen-1:
|
||||
# instGenericContainer can bind `tyVoid`
|
||||
if invocation[i].kind != tyVoid:
|
||||
bindParam(c, m, genericBody[i-1], invocation[i])
|
||||
result = conceptMatchNode(c, concpt.conceptBody, m)
|
||||
if result and mfDontBind notin m.flags:
|
||||
fixBindings(bindings, concpt, invocation, m)
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): 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
|
||||
@@ -377,28 +592,16 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
|
||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
if arg.containsUnresolvedType:
|
||||
return false
|
||||
var m = MatchCon(inferred: @[], potentialImplementation: arg, concpt: concpt)
|
||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
||||
if result:
|
||||
for (a, b) in m.inferred:
|
||||
if b.kind == tyGenericParam:
|
||||
var dest = b
|
||||
while true:
|
||||
dest = existingBinding(m, dest)
|
||||
if dest == nil or dest.kind != tyGenericParam: break
|
||||
if dest != nil:
|
||||
bindings.put(a, dest)
|
||||
when logBindings: echo "A bind ", a, " ", dest
|
||||
else:
|
||||
bindings.put(a, b)
|
||||
when logBindings: echo "B bind ", a, " ", b
|
||||
# we have a match, so bind 'arg' itself to 'concpt':
|
||||
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.put(invocation[i], arg[i])
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
|
||||
if arg.isConcept:
|
||||
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
|
||||
elif arg.acceptsAllTypes:
|
||||
# XXX: I think this is wrong, or at least partially wrong. Can still test ambiguous types
|
||||
result = false
|
||||
elif mfCheckGeneric in m.flags:
|
||||
# prioritize concepts the least. Specifically if the arg is not a catch all as per above
|
||||
result = true
|
||||
else:
|
||||
result = processConcept(c, concpt, invocation, bindings, m)
|
||||
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimUseStrictDefs")
|
||||
defineSymbol("nimUseStrictDefs") # deadcode
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
@@ -171,3 +171,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasJsNoLambdaLifting")
|
||||
defineSymbol("nimHasDefaultFloatRoundtrip")
|
||||
defineSymbol("nimHasXorSet")
|
||||
|
||||
defineSymbol("nimHasSetLengthSeqUninitMagic")
|
||||
defineSymbol("nimHasPreviewDuplicateModuleError")
|
||||
|
||||
|
||||
@@ -439,8 +439,8 @@ proc gen(c: var Con; n: PNode) =
|
||||
genUse(c, n)
|
||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
||||
of nkWhenStmt:
|
||||
# This is "when nimvm" node. Chose the first branch.
|
||||
gen(c, n[0][1])
|
||||
# This is "when nimvm" node. Chose the second branch.
|
||||
gen(c, n[1][0])
|
||||
of nkCaseStmt: genCase(c, n)
|
||||
of nkWhileStmt: genWhile(c, n)
|
||||
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
|
||||
|
||||
@@ -1406,11 +1406,14 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
|
||||
for it in n: traceDeps(d, it)
|
||||
of nkExportStmt:
|
||||
for it in n:
|
||||
# bug #23051; don't generate documentation for exported symbols again
|
||||
if it.kind == nkSym and sfExported notin it.sym.flags:
|
||||
if d.module != nil and d.module == it.sym.owner:
|
||||
generateDoc(d, it.sym.ast, orig, config, kForceExport)
|
||||
if it.kind == nkSym:
|
||||
if d.module != nil and d.module == it.sym.owner: # in current module
|
||||
# bug #23051; don't generate documentation for exported symbols again
|
||||
if sfExported notin it.sym.flags:
|
||||
generateDoc(d, it.sym.ast, orig, config, kForceExport)
|
||||
# else it's to be handled in `of XxxSection` branch
|
||||
elif it.sym.ast != nil:
|
||||
# only export symbols in imported modules, not in current module
|
||||
exportSym(d, it.sym)
|
||||
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
|
||||
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
|
||||
@@ -1889,6 +1892,9 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
|
||||
else:
|
||||
#echo getOutFile(gProjectFull, JsonExt)
|
||||
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
|
||||
conf.outFile = filename.relativeTo(conf.outDir)
|
||||
let dir = filename.splitFile.dir
|
||||
createDir(dir)
|
||||
try:
|
||||
writeFile(filename, content)
|
||||
except IOError:
|
||||
@@ -1909,8 +1915,10 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
||||
if optStdout in d.conf.globalOptions:
|
||||
write(stdout, content)
|
||||
else:
|
||||
#echo getOutFile(gProjectFull, TagsExt)
|
||||
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
|
||||
conf.outFile = filename.relativeTo(conf.outDir)
|
||||
let dir = filename.splitFile.dir
|
||||
createDir(dir)
|
||||
try:
|
||||
writeFile(filename, content)
|
||||
except IOError:
|
||||
|
||||
@@ -33,6 +33,10 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
||||
#newIntTypeNode(nkIntLit, field.position, t)
|
||||
# safety branch for invalid data:
|
||||
caseStmt.add newTree(nkElse,
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res),
|
||||
newStrNode("", info))))
|
||||
|
||||
body.add(caseStmt)
|
||||
|
||||
@@ -43,8 +47,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
n[bodyPos] = body
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfNeverRaises
|
||||
incl result.flagsImpl, {sfFromGeneric, sfNeverRaises}
|
||||
|
||||
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
||||
case obj.kind
|
||||
@@ -106,5 +109,4 @@ proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGra
|
||||
n[bodyPos] = body
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfNeverRaises
|
||||
incl result.flagsImpl, {sfFromGeneric, sfNeverRaises}
|
||||
|
||||
@@ -257,8 +257,8 @@ compiler tcc:
|
||||
linkerExe: "tcc",
|
||||
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
linkDirCmd: " -L",
|
||||
linkLibCmd: " -l$1",
|
||||
debug: " -g ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "asm($1);$n",
|
||||
@@ -341,7 +341,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
|
||||
var fullSuffix = suffix
|
||||
case conf.backend
|
||||
of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix
|
||||
of backendC: discard
|
||||
of backendC, backendNif: discard
|
||||
of backendInvalid:
|
||||
# during parsing of cfg files; we don't know the backend yet, no point in
|
||||
# guessing wrong thing
|
||||
@@ -474,6 +474,11 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
|
||||
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
|
||||
result = conf.compileOptions
|
||||
|
||||
if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
|
||||
conf.selectedGC == gcRefc:
|
||||
# bug #10625
|
||||
addOpt(result, "-fno-omit-frame-pointer")
|
||||
|
||||
for option in conf.compileOptionsCmd:
|
||||
if strutils.find(result, option, 0) < 0:
|
||||
addOpt(result, option)
|
||||
@@ -805,6 +810,59 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
|
||||
(ose.msg & " " & $ose.errorCode))
|
||||
raise
|
||||
|
||||
proc createMacAppBundle(conf: ConfigRef; exefile: AbsoluteFile) =
|
||||
let (dir, name, _) = splitFile(exefile.string)
|
||||
let appBundleName = name & ".app"
|
||||
let appBundlePath = dir / appBundleName
|
||||
let contentsPath = appBundlePath / "Contents"
|
||||
let macosPath = contentsPath / "MacOS"
|
||||
|
||||
createDir(macosPath)
|
||||
|
||||
let bundleExePath = macosPath / name
|
||||
copyFileWithPermissions(exefile.string, bundleExePath)
|
||||
|
||||
let infoPlistPath = contentsPath / "Info.plist"
|
||||
|
||||
proc xmlEscape(s: string): string =
|
||||
result = newStringOfCap(s.len)
|
||||
for c in items(s):
|
||||
case c:
|
||||
of '<': result.add("<")
|
||||
of '>': result.add(">")
|
||||
of '&': result.add("&")
|
||||
of '"': result.add(""")
|
||||
of '\'': result.add("'")
|
||||
else:
|
||||
if ord(c) < 32:
|
||||
result.add("&#" & $ord(c) & ';')
|
||||
else:
|
||||
result.add(c)
|
||||
|
||||
let escapedName = xmlEscape(name)
|
||||
let infoPlistContent = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$1</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.nim.$1</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$1</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>""" % [escapedName]
|
||||
|
||||
writeFile(infoPlistPath, infoPlistContent)
|
||||
|
||||
removeFile(exefile.string)
|
||||
|
||||
rawMessage(conf, hintUserRaw, "Created Mac app bundle: " & appBundlePath)
|
||||
|
||||
proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
|
||||
result = @[]
|
||||
when defined(macosx):
|
||||
@@ -989,6 +1047,10 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
preventLinkCmdMaxCmdLen(conf, linkCmd)
|
||||
for cmd in extraCmds:
|
||||
execExternalProgram(conf, cmd, hintExecuting)
|
||||
# create Mac app bundle for GUI apps on macOS
|
||||
when defined(macosx):
|
||||
if conf.globalOptions * {optGenGuiApp, optGenDynLib, optGenStaticLib} == {optGenGuiApp}:
|
||||
createMacAppBundle(conf, mainOutput)
|
||||
else:
|
||||
linkCmd = ""
|
||||
if optGenScript in conf.globalOptions:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode
|
||||
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode
|
||||
|
||||
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
|
||||
@@ -61,10 +61,11 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
# activate this pattern again:
|
||||
c.patterns[i] = pattern
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode =
|
||||
inc(c.hloLoopDetector)
|
||||
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
|
||||
# simply stop and do not perform any further transformations:
|
||||
if c.hloLoopDetector > 300: return n
|
||||
if loopDetector > 300:
|
||||
message(c.config, n.info, warnUser, "term rewrite macro instantiation too nested")
|
||||
return n
|
||||
case n.kind
|
||||
of nkMacroDef, nkTemplateDef, procDefs:
|
||||
# already processed (special cases in semstmts.nim)
|
||||
@@ -80,7 +81,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
# no optimization applied, try subtrees:
|
||||
for i in 0..<result.safeLen:
|
||||
let a = result[i]
|
||||
let h = hlo(c, a)
|
||||
let h = hlo(c, a, loopDetector)
|
||||
if h != a: result[i] = h
|
||||
else:
|
||||
# perform type checking, so that the replacement still fits:
|
||||
@@ -90,17 +91,15 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
result = fitNode(c, n.typ, result, n.info)
|
||||
# optimization has been applied so check again:
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
result = hlo(c, result)
|
||||
result = hlo(c, result, loopDetector + 1)
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
|
||||
proc hloBody(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
||||
c.hloLoopDetector = 0
|
||||
result = hlo(c, n)
|
||||
result = hlo(c, n, 0)
|
||||
|
||||
proc hloStmt(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
||||
c.hloLoopDetector = 0
|
||||
result = hlo(c, n)
|
||||
result = hlo(c, n, 0)
|
||||
|
||||
1859
compiler/ic/enum2nif.nim
Normal file
1859
compiler/ic/enum2nif.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -899,11 +899,11 @@ proc moduleIndex*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: in
|
||||
proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
s: PackedSym; si, item: int32): PSym =
|
||||
result = PSym(itemId: ItemId(module: si, item: item),
|
||||
kind: s.kind, magic: s.magic, flags: s.flags,
|
||||
info: translateLineInfo(c, g, si, s.info),
|
||||
options: s.options,
|
||||
position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position,
|
||||
offset: if s.kind in routineKinds: defaultOffset else: s.offset,
|
||||
kindImpl: s.kind, magicImpl: s.magic, flagsImpl: s.flags,
|
||||
infoImpl: translateLineInfo(c, g, si, s.info),
|
||||
optionsImpl: s.options,
|
||||
positionImpl: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position,
|
||||
offsetImpl: if s.kind in routineKinds: defaultOffset else: s.offset,
|
||||
disamb: s.disamb,
|
||||
name: getIdent(c.cache, g[si].fromDisk.strings[s.name])
|
||||
)
|
||||
@@ -945,8 +945,8 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
setOwner(result, loadSym(c, g, si, s.owner))
|
||||
let externalName = g[si].fromDisk.strings[s.externalName]
|
||||
if externalName != "":
|
||||
result.loc.snippet = externalName
|
||||
result.loc.flags = s.locFlags
|
||||
result.locImpl.snippet = externalName
|
||||
result.locImpl.flags = s.locFlags
|
||||
result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
|
||||
|
||||
proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
@@ -990,10 +990,10 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s:
|
||||
proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
t: PackedType; si, item: int32): PType =
|
||||
result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind,
|
||||
flags: t.flags, size: t.size, align: t.align,
|
||||
paddingAtEnd: t.paddingAtEnd,
|
||||
flagsImpl: t.flags, sizeImpl: t.size, alignImpl: t.align,
|
||||
paddingAtEndImpl: t.paddingAtEnd,
|
||||
uniqueId: ItemId(module: si, item: item),
|
||||
callConv: t.callConv)
|
||||
callConvImpl: t.callConv)
|
||||
|
||||
proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
t: PackedType; si, item: int32; result: PType) =
|
||||
@@ -1058,12 +1058,12 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
|
||||
let filename = AbsoluteFile toFullPath(conf, fileIdx)
|
||||
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
||||
# mechanism, which we do in order to assign each module a persistent ID.
|
||||
m.module = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
|
||||
m.module = PSym(kindImpl: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
|
||||
name: getIdent(cache, splitFile(filename).name),
|
||||
info: newLineInfo(fileIdx, 1, 1),
|
||||
position: int(fileIdx))
|
||||
infoImpl: newLineInfo(fileIdx, 1, 1),
|
||||
positionImpl: int(fileIdx))
|
||||
setOwner(m.module, getPackage(conf, cache, fileIdx))
|
||||
m.module.flags = m.fromDisk.moduleFlags
|
||||
m.module.flagsImpl = m.fromDisk.moduleFlags
|
||||
|
||||
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
fileIdx: FileIndex; m: var LoadedModule) =
|
||||
|
||||
@@ -245,7 +245,8 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden, track
|
||||
# avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
|
||||
result = createModuleAliasImpl(realModule.name)
|
||||
if importHidden:
|
||||
result.options.incl optImportHidden
|
||||
ensureMutable result
|
||||
result.optionsImpl.incl optImportHidden
|
||||
let moduleIdent = if n.kind in {nkInfix, nkImportAs}: n[^1] else: n
|
||||
result.info = moduleIdent.info
|
||||
if trackUnusedImport:
|
||||
|
||||
@@ -24,7 +24,7 @@ import std/[strtabs, tables, strutils, intsets]
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
from trees import exprStructuralEquivalent, getRoot, whichPragma
|
||||
from trees import exprStructuralEquivalent, getRoot, whichPragma, getPotentialWrites
|
||||
|
||||
type
|
||||
Con = object
|
||||
@@ -100,6 +100,7 @@ when false:
|
||||
proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
let root = parampatterns.exprRoot(n, allowCalls=false)
|
||||
if root == nil: return false
|
||||
elif sfSingleUsedTemp in root.flags: return true
|
||||
|
||||
var s = addr(scope)
|
||||
while s != nil:
|
||||
@@ -162,13 +163,16 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
|
||||
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
|
||||
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
# bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
|
||||
if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
|
||||
if not hasDestructorOrAsgn(c, n.typ): return true
|
||||
|
||||
let m = skipConvDfa(n)
|
||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
||||
isLastReadImpl(n, c, s)
|
||||
result = isLastReadImpl(n, c, s)
|
||||
|
||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||
let m = skipConvDfa(n)
|
||||
@@ -185,10 +189,11 @@ proc isCursor(n: PNode): bool =
|
||||
else:
|
||||
false
|
||||
|
||||
template isUnpackedTuple(n: PNode): bool =
|
||||
template isFullyUnpackedTuple(n: PNode): bool =
|
||||
## we move out all elements of unpacked tuples,
|
||||
## hence unpacked tuples themselves don't need to be destroyed
|
||||
## except it's already a cursor
|
||||
## restricted to `skTemp`, tuple temps where not every field is unpacked should not use `skTemp`
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and
|
||||
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
|
||||
|
||||
@@ -197,7 +202,8 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFr
|
||||
if inferredFromCopy:
|
||||
m.add ", which is inferred from unavailable '=copy'"
|
||||
|
||||
if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
|
||||
if (opname == "=" or opname == "=copy" or opname == "=dup") and
|
||||
ri != nil:
|
||||
m.add "; requires a copy because it's not the last read of '"
|
||||
m.add renderTree(ri)
|
||||
m.add '\''
|
||||
@@ -247,7 +253,12 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
|
||||
dbg:
|
||||
if kind == attachedDestructor:
|
||||
echo "destructor is ", op.id, " ", op.ast
|
||||
if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||
if sfError in op.flags:
|
||||
if ri != nil:
|
||||
checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
|
||||
else:
|
||||
# uses the lineinfos of `dest` is `ri` is not available
|
||||
checkForErrorPragma(c, t, dest, AttachedOpToStr[kind])
|
||||
c.genOp(op, dest)
|
||||
|
||||
proc genDestroy(c: var Con; dest: PNode): PNode =
|
||||
@@ -275,7 +286,7 @@ proc deepAliases(dest, ri: PNode): bool =
|
||||
return aliases(dest, ri) != no
|
||||
|
||||
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
|
||||
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
|
||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||
isNoInit(dest) or IsReturn in flags:
|
||||
# optimize sink call into a bitwise memcopy
|
||||
@@ -400,7 +411,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode =
|
||||
result = genOp(c, op, n)
|
||||
else:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
|
||||
result.add copyTree(n) #mWasMoved does not take the address
|
||||
#if n.kind != nkSym:
|
||||
# message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
|
||||
@@ -449,7 +460,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
|
||||
let tmp = c.getTemp(s, nTyp, n.info)
|
||||
if hasDestructor(c, nTyp):
|
||||
if hasDestructorOrAsgn(c, nTyp):
|
||||
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil and tfHasOwned notin typ.flags:
|
||||
@@ -559,7 +570,7 @@ proc cycleCheck(n: PNode; c: var Con) =
|
||||
proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
|
||||
# move the variable declaration to the top of the frame:
|
||||
s.vars.add v.sym
|
||||
if isUnpackedTuple(v):
|
||||
if isFullyUnpackedTuple(v):
|
||||
if c.inLoop > 0:
|
||||
# unpacked tuple needs reset at every loop iteration
|
||||
res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
|
||||
@@ -935,6 +946,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
of nkVarSection, nkLetSection:
|
||||
# transform; var x = y to var x; x op y where op is a move or copy
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
let isInProc = c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}
|
||||
|
||||
for it in n:
|
||||
var ri = it[^1]
|
||||
if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
|
||||
@@ -950,7 +964,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
s.locals.add v.sym
|
||||
pVarTopLevel(v, c, s, result)
|
||||
if ri.kind != nkEmpty:
|
||||
result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
let isGlobalPragma = v.kind == nkSym and
|
||||
{sfPure, sfGlobal} <= v.sym.flags and
|
||||
isInProc
|
||||
|
||||
if isGlobalPragma:
|
||||
c.graph.procGlobals.add n
|
||||
else:
|
||||
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
result.add value
|
||||
elif ri.kind == nkEmpty and c.inLoop > 0:
|
||||
let skipInit = v.kind == nkDotExpr and # Closure var
|
||||
sfNoInit in v[1].sym.flags
|
||||
@@ -1122,6 +1144,25 @@ proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags:
|
||||
var snk = c.genSink(s, dest, newAccess, flags)
|
||||
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
|
||||
|
||||
proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode =
|
||||
var n = orig
|
||||
while true:
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
n = n[0]
|
||||
else:
|
||||
break
|
||||
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
|
||||
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
|
||||
let tmp = c.getTemp(s, n.typ, n.info)
|
||||
tmp.sym.flagsImpl.incl sfSingleUsedTemp
|
||||
result.add newTree(nkFastAsgn, tmp, copyTree(n))
|
||||
s.final.add c.genDestroy(tmp)
|
||||
n[] = tmp[]
|
||||
result.add copyTree(orig)
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
var ri = ri
|
||||
var isEnsureMove = 0
|
||||
@@ -1148,7 +1189,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
of nkCallKinds:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkBracketExpr:
|
||||
if isUnpackedTuple(ri[0]):
|
||||
if isFullyUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
||||
@@ -1197,15 +1238,22 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
result.add p(ri, c, s, consumed)
|
||||
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:
|
||||
if IsExplicitSink in flags:
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
else:
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock:
|
||||
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
|
||||
# We know the result will be a stmt so we use that fact to optimize
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
of nkRaiseStmt:
|
||||
result = pRaiseStmt(ri, c, s)
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
let isOwnsData = ownsData(c, s, ri2, flags)
|
||||
|
||||
if isOwnsData != nil:
|
||||
result = moveOrCopy(dest, isOwnsData, c, s, flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
|
||||
canBeMoved(c, dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(s, dest, ri, flags)
|
||||
@@ -1244,6 +1292,55 @@ when false:
|
||||
for i in 0..<n.safeLen:
|
||||
injectDefaultCalls(n[i], c)
|
||||
|
||||
proc replaceSinkParam(n: PNode, mapping: Table[int, PSym]): PNode =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.id in mapping:
|
||||
result = newSymNode(mapping[n.sym.id])
|
||||
else:
|
||||
result = n
|
||||
of nkVarSection, nkLetSection:
|
||||
result = copyNode(n)
|
||||
newSons(result, n.len)
|
||||
for i in 0..<n.len:
|
||||
result[i] = copyNode(n[i])
|
||||
for j in 0..<n[i].len-1:
|
||||
result[i].add n[i][j]
|
||||
result[i].add replaceSinkParam(n[i][^1], mapping)
|
||||
of {nkNone..nkNilLit}-{nkSym}, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
result = n
|
||||
else:
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
result.add replaceSinkParam(n[i], mapping)
|
||||
|
||||
proc addSinkCopy(c: var Con; s: var Scope; sinkParams: seq[PSym]; n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
var mapping = initTable[int, PSym]()
|
||||
|
||||
var mutated = newSeq[PNode]()
|
||||
getPotentialWrites(n, false, mutated)
|
||||
var mutatedSet = initIntSet()
|
||||
for m in mutated:
|
||||
mutatedSet.incl m.sym.id
|
||||
for param in sinkParams:
|
||||
if param.id in mutatedSet:
|
||||
let newSym = newSym(skTemp, getIdent(c.graph.cache, "sinkCopy"), c.idgen, param.owner, n.info)
|
||||
newSym.flagsImpl.incl sfFromGeneric
|
||||
newSym.typ = param.typ.elementType
|
||||
mapping[param.id] = newSym
|
||||
let v = newNodeI(nkVarSection, n.info)
|
||||
v.addVar(newSymNode(newSym), newSymNode(param))
|
||||
result.add v
|
||||
if mapping.len > 0:
|
||||
result.add replaceSinkParam(n, mapping)
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
|
||||
when toDebug.len > 0:
|
||||
shouldDebug = toDebug == owner.name.s or toDebug == "always"
|
||||
@@ -1257,15 +1354,24 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n:
|
||||
var scope = Scope(body: n)
|
||||
let body = p(n, c, scope, normal)
|
||||
|
||||
var sinkParams = newSeq[PSym]()
|
||||
|
||||
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
|
||||
let params = owner.typ.n
|
||||
for i in 1..<params.len:
|
||||
let t = params[i].sym.typ
|
||||
if isSinkTypeForParam(t) and hasDestructor(c, t.skipTypes({tySink})):
|
||||
scope.final.add c.genDestroy(params[i])
|
||||
if isSinkTypeForParam(t):
|
||||
let baseType = t.skipTypes({tySink})
|
||||
if baseType.kind in {tyString, tySequence, tyArray, tyTuple, tyObject}:
|
||||
sinkParams.add params[i].sym
|
||||
if hasDestructor(c, baseType):
|
||||
scope.final.add c.genDestroy(params[i])
|
||||
#if optNimV2 in c.graph.config.globalOptions:
|
||||
# injectDefaultCalls(n, c)
|
||||
result = optimize processScope(c, scope, body)
|
||||
if sinkParams.len > 0:
|
||||
result = addSinkCopy(c, scope, sinkParams, result)
|
||||
|
||||
dbg:
|
||||
echo ">---------transformed-to--------->"
|
||||
echo renderTree(result, {renderIds})
|
||||
|
||||
122
compiler/inliner.nim
Normal file
122
compiler/inliner.nim
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
proc copySymdef(n: PNode; locals: var Table[int, PSym]; idgen: IdGenerator; owner: PSym): PNode =
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
|
||||
result = n
|
||||
of nkSym:
|
||||
let oldSym = n.sym
|
||||
let newSym = copySym(oldSym, idgen)
|
||||
setOwner(newSym, owner)
|
||||
locals[oldSym.id] = newSym
|
||||
result = newSymNode(newSym, oldSym.info)
|
||||
else:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = copySymdef(n[i], locals, idgen, owner)
|
||||
|
||||
proc copyInlineProcBody(n: PNode; locals: var Table[int, PSym]; idgen: IdGenerator; owner: PSym): PNode =
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
|
||||
result = n
|
||||
of nkSym:
|
||||
let sym = locals.getOrDefault(n.sym.id)
|
||||
if sym != nil:
|
||||
result = newSymNode(sym, n.info)
|
||||
else:
|
||||
result = n
|
||||
of nkLetSection, nkVarSection:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind == nkCommentStmt:
|
||||
result[i] = it
|
||||
elif it.kind in {nkIdentDefs, nkConstDef}:
|
||||
result[i] = shallowCopy(it)
|
||||
for j in 0..<it.len-2:
|
||||
result[i][j] = copySymdef(it[j], locals, idgen, owner)
|
||||
for j in it.len-2..<it.len:
|
||||
result[i][j] = copyInlineProcBody(it[j], locals, idgen, owner)
|
||||
else:
|
||||
assert it.kind == nkVarTuple
|
||||
result[i] = shallowCopy(it)
|
||||
for j in 0..<it.len-2:
|
||||
assert it[j].kind == nkSym
|
||||
let oldSym = it[j].sym
|
||||
let newSym = copySym(oldSym, idgen)
|
||||
setOwner(newSym, owner)
|
||||
locals[oldSym.id] = newSym
|
||||
result[i][j] = newSymNode(newSym, oldSym.info)
|
||||
for j in it.len-2..<it.len:
|
||||
result[i][j] = copyInlineProcBody(it[j], locals, idgen, owner)
|
||||
|
||||
of nkForStmt, nkParForStmt:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len-2:
|
||||
assert n[i].kind == nkSym
|
||||
let oldSym = n[i].sym
|
||||
let newSym = copySym(oldSym, idgen)
|
||||
setOwner(newSym, owner)
|
||||
locals[oldSym.id] = newSym
|
||||
result[i] = newSymNode(newSym, oldSym.info)
|
||||
result[n.len-2] = copyInlineProcBody(n[n.len-2], locals, idgen, owner)
|
||||
result[n.len-1] = copyInlineProcBody(n[n.len-1], locals, idgen, owner)
|
||||
of routineDefs, nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt, nkConstSection:
|
||||
result = n
|
||||
else:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = copyInlineProcBody(n[i], locals, idgen, owner)
|
||||
|
||||
proc copyParams(n: PNode; locals: var Table[int, PSym]; idgen: IdGenerator; owner: PSym): PNode =
|
||||
result = shallowCopy(n)
|
||||
result[0] = n[0] # return type
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
assert it.kind == nkIdentDefs
|
||||
result[i] = shallowCopy(it)
|
||||
for j in 0..<it.len-2:
|
||||
assert it[j].kind == nkSym
|
||||
let oldSym = it[j].sym
|
||||
let newSym = copySym(oldSym, idgen)
|
||||
setOwner(newSym, owner)
|
||||
locals[oldSym.id] = newSym
|
||||
result[i][j] = newSymNode(newSym, oldSym.info)
|
||||
owner.typ.addParam newSym
|
||||
for j in it.len-2..<it.len:
|
||||
result[i][j] = copyInlineProcBody(it[j], locals, idgen, owner)
|
||||
|
||||
proc copyInlineProc(prc: PSym; idgen: IdGenerator): PSym =
|
||||
result = copySym(prc, idgen)
|
||||
var locals = initTable[int, PSym]()
|
||||
|
||||
var a = shallowCopy(prc.ast)
|
||||
if resultPos < prc.ast.len and prc.ast[resultPos].kind == nkSym:
|
||||
let oldRes = prc.ast[resultPos].sym
|
||||
let newRes = copySym(oldRes, idgen)
|
||||
setOwner(newRes, result)
|
||||
locals[oldRes.id] = newRes
|
||||
a[resultPos] = newSymNode(newRes, oldRes.info)
|
||||
|
||||
result.typ = copyType(prc.typ, idgen, result)
|
||||
result.typ.n = newNodeI(prc.typ.n.kind, prc.typ.n.info)
|
||||
if prc.typ.n.len > 0:
|
||||
result.typ.n.add copyNode(prc.typ.n[0])
|
||||
for i in 1..<prc.typ.n.len:
|
||||
let it = prc.typ.n[i]
|
||||
assert it.kind == nkSym
|
||||
let oldSym = it.sym
|
||||
let newSym = copySym(oldSym, idgen)
|
||||
setOwner(newSym, result)
|
||||
locals[oldSym.id] = newSym
|
||||
result.typ.addParam newSym
|
||||
|
||||
for i in 0..<prc.ast.len:
|
||||
if i == paramsPos:
|
||||
a[i] = copyTree(prc.ast[i])
|
||||
elif i == resultPos and prc.ast[i].kind == nkSym:
|
||||
discard "handled above"
|
||||
else:
|
||||
a[i] = copyInlineProcBody(prc.ast[i], locals, idgen, result)
|
||||
result.ast = a
|
||||
|
||||
#echo "Produced: ", renderTree(result.ast, {renderIds})
|
||||
@@ -6,7 +6,7 @@ Name: "Nim"
|
||||
Version: "$version"
|
||||
Platforms: """
|
||||
windows: i386;amd64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;s390x;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
@@ -80,6 +80,7 @@ Files: "lib"
|
||||
Files: "examples"
|
||||
Files: "dist/nimble"
|
||||
Files: "dist/checksums"
|
||||
Files: "dist/nimony"
|
||||
|
||||
Files: "tests"
|
||||
|
||||
|
||||
@@ -277,7 +277,8 @@ proc mangleName(m: BModule, s: PSym): Rope =
|
||||
else:
|
||||
result.add("_")
|
||||
result.add(rope(s.id))
|
||||
s.loc.snippet = result
|
||||
ensureMutable s
|
||||
s.locImpl.snippet = result
|
||||
|
||||
proc escapeJSString(s: string): string =
|
||||
result = newStringOfCap(s.len + s.len shr 2)
|
||||
@@ -1002,7 +1003,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# If some branch requires a local alias introduce it here. This is needed
|
||||
# since JS cannot do ``catch x as y``.
|
||||
if excAlias != nil:
|
||||
excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym)
|
||||
ensureMutable excAlias.sym
|
||||
excAlias.sym.locImpl.snippet = mangleName(p.module, excAlias.sym)
|
||||
lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet)
|
||||
gen(p, n[i][^1], a)
|
||||
moveInto(p, a, r)
|
||||
@@ -1135,7 +1137,8 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# named block?
|
||||
if (n[0].kind != nkSym): internalError(p.config, n.info, "genBlock")
|
||||
var sym = n[0].sym
|
||||
sym.loc.k = locOther
|
||||
ensureMutable sym
|
||||
sym.locImpl.k = locOther
|
||||
sym.position = idx+1
|
||||
let labl = p.unique
|
||||
lineF(p, "Label$1: {$n", [labl.rope])
|
||||
@@ -1234,7 +1237,8 @@ proc generateHeader(p: PProc, prc: PSym): Rope =
|
||||
# to keep it simple
|
||||
let env = prc.ast[paramsPos].lastSon
|
||||
assert env.kind == nkSym, "env is missing"
|
||||
env.sym.loc.snippet = "this"
|
||||
ensureMutable env.sym
|
||||
env.sym.locImpl.snippet = "this"
|
||||
|
||||
for i in 1..<typ.n.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
@@ -1371,12 +1375,15 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyBaseIndex
|
||||
let b = if n.kind == nkHiddenAddr: n[0] else: n
|
||||
gen(p, b[0], a)
|
||||
if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
|
||||
if skipTypes(b[0].typ, abstractVarRange + tyTypeClasses).kind == tyTuple:
|
||||
# ref #25227 about `+ tyTypeClasses`
|
||||
r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
|
||||
else:
|
||||
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
|
||||
var f = b[1].sym
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
if f.loc.snippet == "":
|
||||
ensureMutable f
|
||||
f.locImpl.snippet = mangleName(p.module, f)
|
||||
r.res = makeJSString($f.loc.snippet)
|
||||
internalAssert p.config, a.typ != etyBaseIndex
|
||||
r.address = a.res
|
||||
@@ -1404,7 +1411,9 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else:
|
||||
if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
|
||||
var f = n[1].sym
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
if f.loc.snippet == "":
|
||||
ensureMutable f
|
||||
f.locImpl.snippet = mangleName(p.module, f)
|
||||
r.res = "$1.$2" % [r.res, f.loc.snippet]
|
||||
mkTemp(1)
|
||||
r.kind = resExpr
|
||||
@@ -1425,11 +1434,15 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
|
||||
# Field symbol
|
||||
var field = accessExpr[1].sym
|
||||
internalAssert p.config, field.kind == skField
|
||||
if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field)
|
||||
if field.loc.snippet == "":
|
||||
ensureMutable field
|
||||
field.locImpl.snippet = mangleName(p.module, field)
|
||||
# Discriminant symbol
|
||||
let disc = checkExpr[2].sym
|
||||
internalAssert p.config, disc.kind == skField
|
||||
if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc)
|
||||
if disc.loc.snippet == "":
|
||||
ensureMutable disc
|
||||
disc.locImpl.snippet = mangleName(p.module, disc)
|
||||
|
||||
var setx: TCompRes = default(TCompRes)
|
||||
gen(p, checkExpr[1], setx)
|
||||
@@ -1586,7 +1599,11 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkObjDownConv:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
gen(p, n[0], r)
|
||||
if n.kind in {nkAddr, nkHiddenAddr}:
|
||||
# addr ( deref ( x )) --> x
|
||||
gen(p, n[0][0], r)
|
||||
else:
|
||||
gen(p, n[0], r)
|
||||
of nkHiddenAddr:
|
||||
gen(p, n[0], r)
|
||||
of nkConv:
|
||||
@@ -1837,7 +1854,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
|
||||
proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
|
||||
# don't call '$' here for efficiency:
|
||||
let f = n[0].sym
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
if f.loc.snippet == "":
|
||||
ensureMutable f
|
||||
f.locImpl.snippet = mangleName(p.module, f)
|
||||
if sfInfixCall in f.flags:
|
||||
let pat = $n[0].sym.loc.snippet
|
||||
internalAssert p.config, pat.len > 0
|
||||
@@ -2329,8 +2348,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" %
|
||||
[b, lhs.rdLoc, tmp]
|
||||
else:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
|
||||
r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
|
||||
useMagic(p, "nimAddStrStr")
|
||||
r.res = "nimAddStrStr($1, $2);" % [lhs.rdLoc, rhs.rdLoc]
|
||||
r.kind = resExpr
|
||||
of mAppendSeqElem:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
@@ -2434,7 +2453,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
binaryExpr(p, n, r, "mnewString",
|
||||
"""if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
|
||||
else {$3.length = $4; }""")
|
||||
of mSetLengthSeq:
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
@@ -2573,7 +2592,9 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let val = it[1]
|
||||
gen(p, val, a)
|
||||
var f = it[0].sym
|
||||
if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
|
||||
if f.loc.snippet == "":
|
||||
ensureMutable f
|
||||
f.locImpl.snippet = mangleName(p.module, f)
|
||||
fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
|
||||
|
||||
let typ = val.typ.skipTypes(abstractInst)
|
||||
@@ -2875,6 +2896,8 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
elif dest.kind in tyFloat..tyFloat64:
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number($1)" % [r.res]
|
||||
elif dest.kind == tyChar and (fromInt or fromUint):
|
||||
r.res = "($1 & 255)" % [r.res]
|
||||
elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
|
||||
r.address = r.res
|
||||
r.res = "null"
|
||||
|
||||
@@ -122,7 +122,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||
[name, genTypeInfo(p, typ.baseClass)])
|
||||
|
||||
proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
|
||||
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned} + tyUserTypeClasses)
|
||||
result = "NTI$1" % [rope(t.id)]
|
||||
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
|
||||
case t.kind
|
||||
|
||||
@@ -150,6 +150,7 @@ template isIterator*(owner: PSym): bool =
|
||||
|
||||
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
|
||||
result = createObj(g, idgen, owner, info, final=false)
|
||||
result.incl tfFinal
|
||||
if owner.isIterator:
|
||||
rawAddField(result, createStateField(g, owner, idgen))
|
||||
|
||||
@@ -160,7 +161,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym
|
||||
# XXX a bit hacky:
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
|
||||
result.typ = iter.typ.returnType
|
||||
incl(result.flags, sfUsed)
|
||||
incl(result.flagsImpl, sfUsed)
|
||||
iter.ast.add newSymNode(result)
|
||||
|
||||
proc addHiddenParam(routine: PSym, param: PSym) =
|
||||
@@ -199,7 +200,7 @@ proc interestingVar(s: PSym): bool {.inline.} =
|
||||
proc illegalCapture(s: PSym): bool {.inline.} =
|
||||
result = classifyViewType(s.typ) != noView or s.kind == skResult
|
||||
|
||||
proc isInnerProc(s: PSym): bool =
|
||||
proc isInnerProc*(s: PSym): bool =
|
||||
if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
|
||||
result = s.skipGenericOwner.kind in routineKinds
|
||||
else:
|
||||
@@ -227,7 +228,7 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
|
||||
#if isClosureIterator(result.typ):
|
||||
createTypeBoundOps(g, nil, result.typ, info, idgen)
|
||||
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
|
||||
prc.flags.incl sfInjectDestructors
|
||||
prc.incl sfInjectDestructors
|
||||
|
||||
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
|
||||
## lambda lifting can be harmful for JS-like code generators.
|
||||
@@ -239,7 +240,7 @@ proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen
|
||||
createTypeBoundOps(g, nil, refType.elementType, info, idgen)
|
||||
createTypeBoundOps(g, nil, refType, info, idgen)
|
||||
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
|
||||
owner.flags.incl sfInjectDestructors
|
||||
owner.incl sfInjectDestructors
|
||||
|
||||
proc genCreateEnv(env: PNode): PNode =
|
||||
var c = newNodeIT(nkObjConstr, env.info, env.typ)
|
||||
@@ -289,7 +290,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
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])
|
||||
incl(owner.typ.flags, tfCapturesEnv)
|
||||
incl(owner.typ, tfCapturesEnv)
|
||||
if not isEnv:
|
||||
owner.typ.callConv = ccClosure
|
||||
|
||||
@@ -335,7 +336,7 @@ proc asOwnedRef(c: var DetectionPass; t: PType): PType =
|
||||
if optOwnedRefs in c.graph.config.globalOptions:
|
||||
assert t.kind == tyRef
|
||||
result = newType(tyOwned, c.idgen, t.owner)
|
||||
result.flags.incl tfHasOwned
|
||||
result.incl tfHasOwned
|
||||
result.rawAddSon t
|
||||
else:
|
||||
result = t
|
||||
@@ -413,7 +414,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
let t = c.getEnvTypeForOwner(owner, info)
|
||||
if cp == nil:
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info)
|
||||
incl(cp.flags, sfFromGeneric)
|
||||
incl(cp.flagsImpl, sfFromGeneric)
|
||||
cp.typ = t
|
||||
addHiddenParam(fn, cp)
|
||||
elif cp.typ != t and fn.kind != skIterator:
|
||||
@@ -623,7 +624,7 @@ proc rawClosureCreation(owner: PSym;
|
||||
if owner.kind != skMacro:
|
||||
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
|
||||
if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
|
||||
owner.flags.incl sfInjectDestructors
|
||||
owner.incl sfInjectDestructors
|
||||
|
||||
let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
||||
if upField != nil:
|
||||
@@ -665,7 +666,7 @@ proc closureCreationForIter(owner: PSym, iter: PNode;
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let iterOwner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, iterOwner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
incl(v.flagsImpl, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
if iterOwner.isIterator:
|
||||
@@ -974,6 +975,20 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
for i in 0..<op.len-1:
|
||||
result.add op[i]
|
||||
|
||||
elif op.kind != nkSym: # might have side effects
|
||||
# bug #25046
|
||||
# create a temp for the closure
|
||||
# var :closureTemp
|
||||
# :closureTemp = ...
|
||||
let tempSym = newSym(skLet, getIdent(g.cache, ":closureTemp"), idgen, owner, body.info)
|
||||
tempSym.typ = call[0].typ
|
||||
let temp = newSymNode(tempSym)
|
||||
var v = newNodeI(nkVarSection, body.info)
|
||||
addVar(v, temp)
|
||||
result.add(v)
|
||||
result.add newAsgnStmt(temp, call[0], body.info)
|
||||
call[0] = temp
|
||||
|
||||
var loopBody = newNodeI(nkStmtList, body.info, 3)
|
||||
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
|
||||
whileLoop[0] = newIntTypeNode(1, getSysType(g, body.info, tyBool))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import std/tables
|
||||
import ast
|
||||
import std/[tables]
|
||||
import ast, astalgo
|
||||
|
||||
type
|
||||
LayeredIdTableObj* {.acyclic.} = object
|
||||
@@ -28,14 +28,15 @@ 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)
|
||||
#copyIdTable(result.topLayer, pt.topLayer)
|
||||
|
||||
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
|
||||
pt.previousLen + pt.topLayer.counter
|
||||
|
||||
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
|
||||
result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen)
|
||||
result = LayeredIdTable(topLayer: initTypeMapping(), previousLen: pt.currentLen)
|
||||
when useRef:
|
||||
result.nextLayer = pt
|
||||
else:
|
||||
@@ -53,6 +54,15 @@ proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
|
||||
let tmp = pt.nextLayer[]
|
||||
pt = tmp
|
||||
|
||||
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
|
||||
var tm = pt
|
||||
while true:
|
||||
for (k, v) in idTablePairs(tm.topLayer):
|
||||
yield (k, v)
|
||||
if tm.nextLayer == nil:
|
||||
break
|
||||
tm.setToPreviousLayer
|
||||
|
||||
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType =
|
||||
result = nil
|
||||
var tm = typeMap
|
||||
|
||||
@@ -40,7 +40,7 @@ template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
|
||||
info: TLineInfo; idgen: IdGenerator): PSym
|
||||
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
idgen: IdGenerator)
|
||||
@@ -91,7 +91,7 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
call.typ() = t
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
||||
if x.kind == nkHiddenDeref:
|
||||
@@ -148,7 +148,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
if sfNeverRaises notin op.flags:
|
||||
c.canRaise = true
|
||||
if c.addMemReset:
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "`=wasMoved`", x))
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
|
||||
else:
|
||||
result = destroy
|
||||
|
||||
@@ -168,7 +168,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||
else:
|
||||
if enforceWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x.dotField(f))
|
||||
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
@@ -218,23 +218,38 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
|
||||
c.filterDiscriminator = oldfilterDiscriminator
|
||||
of nkRecList:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
# destroys in reverse order #24719
|
||||
if c.kind == attachedDestructor:
|
||||
for i in countdown(n.len-1, 0):
|
||||
fillBodyObj(c, n[i], body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
illFormedAstLocal(n, c.g.config)
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.baseClass != nil:
|
||||
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
|
||||
template fillBase =
|
||||
if t.baseClass != nil:
|
||||
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)
|
||||
fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
|
||||
template fillFields =
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
if c.kind == attachedDestructor:
|
||||
# destroys in reverse order #24719
|
||||
fillFields()
|
||||
fillBase()
|
||||
else:
|
||||
fillBase()
|
||||
fillFields()
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
var hasCase = isCaseObj(t.n)
|
||||
@@ -269,7 +284,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
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
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp, sfFromGeneric)
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
let blob = newSymNode(temp)
|
||||
v.addVar(blob, x)
|
||||
@@ -277,7 +292,8 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
#body.add newAsgnStmt(blob, x)
|
||||
|
||||
var wasMovedCall = newNodeI(nkCall, c.info)
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
|
||||
|
||||
wasMovedCall.add x # mWasMoved does not take the address
|
||||
body.add wasMovedCall
|
||||
|
||||
@@ -377,7 +393,8 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
if op != nil and op != c.fn and
|
||||
(sfOverridden in op.flags or destructorOverridden):
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
ensureMutable c.fn
|
||||
incl c.fn.flagsImpl, sfError
|
||||
#else:
|
||||
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
@@ -403,7 +420,8 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
if op == nil:
|
||||
op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
ensureMutable c.fn
|
||||
incl c.fn.flagsImpl, sfError
|
||||
#else:
|
||||
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
@@ -519,7 +537,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = getSysType(c.g, body.info, tyInt)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flagsImpl, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
result = newSymNode(temp)
|
||||
@@ -529,7 +547,7 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = value.typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flagsImpl, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
result = newSymNode(temp)
|
||||
@@ -612,7 +630,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if canFormAcycle(c.g, t.elemType):
|
||||
# follow all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||
@@ -650,7 +668,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, op, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
# XXX: replace these with assertions.
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
@@ -672,7 +690,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
discard "strings are atomic and have no inner elements that are to trace"
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc cyclicType*(g: ModuleGraph, t: PType): bool =
|
||||
case t.kind
|
||||
@@ -771,7 +789,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# If the ref is polymorphic we have to account for this
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
|
||||
#echo "can follow ", elemType, " static ", isFinal(elemType)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
of attachedDup:
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
@@ -838,7 +856,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace:
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
@@ -866,7 +884,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
@@ -894,7 +912,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, x, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind == attachedDeepCopy:
|
||||
@@ -934,7 +952,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
@@ -952,7 +970,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, xx, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
@@ -1002,9 +1020,13 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# 'selectedGC' here to determine if we have the new runtime.
|
||||
discard considerUserDefinedOp(c, t, body, x, y)
|
||||
elif tfHasAsgn in t.flags:
|
||||
# seqs with elements using custom hooks in refc
|
||||
if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}:
|
||||
body.add newSeqCall(c, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
if c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
else:
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyString:
|
||||
@@ -1021,9 +1043,11 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of {attachedAsgn, attachedSink, attachedDup}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
elif tfUnion in t.flags: # bug #25236
|
||||
defaultOp(c, t, body, x, y)
|
||||
else:
|
||||
if c.kind == attachedDup:
|
||||
var op2 = getAttachedOp(c.g, t, attachedAsgn)
|
||||
@@ -1063,9 +1087,7 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ.elementType
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
# TODO: fixme `isDistinct` is a fix for #23552; remove it after
|
||||
# `-d:nimPreviewNonVarDestructor` becomes the default
|
||||
discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
@@ -1100,11 +1122,10 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
|
||||
n[bodyPos] = newNodeI(nkStmtList, info)
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfGeneratedOp
|
||||
incl result.flagsImpl, {sfFromGeneric, sfGeneratedOp}
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
@@ -1115,7 +1136,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
idgen, result, info)
|
||||
|
||||
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
@@ -1143,11 +1164,11 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
n[paramsPos] = result.typ.n
|
||||
n[bodyPos] = newNodeI(nkStmtList, info)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfGeneratedOp
|
||||
incl result.flagsImpl, sfFromGeneric
|
||||
incl result.flagsImpl, sfGeneratedOp
|
||||
if kind == attachedWasMoved:
|
||||
incl result.flags, sfNoSideEffect
|
||||
incl result.typ.flags, tfNoSideEffect
|
||||
incl result.flagsImpl, sfNoSideEffect
|
||||
incl result.typ, tfNoSideEffect
|
||||
|
||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
||||
@@ -1157,13 +1178,13 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(g, c, typ, kind, info, idgen)
|
||||
|
||||
result = getAttachedOp(g, typ, kind)
|
||||
if result == nil:
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
|
||||
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
@@ -1180,7 +1201,8 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
|
||||
if kind == attachedSink and destructorOverridden(g, typ):
|
||||
## compiler can use a combination of `=destroy` and memCopy for sink op
|
||||
dest.flags.incl sfCursor
|
||||
ensureMutable dest
|
||||
dest.flagsImpl.incl sfCursor
|
||||
let op = getAttachedOp(g, typ, attachedDestructor)
|
||||
result.ast[bodyPos].add newOpCall(a, op, if op.typ.firstParamType.kind == tyVar: d[0] else: d)
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
@@ -1197,16 +1219,20 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
||||
else:
|
||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
if not a.canRaise:
|
||||
incl result.flags, sfNeverRaises
|
||||
ensureMutable result
|
||||
incl result.flagsImpl, sfNeverRaises
|
||||
result.ast[pragmasPos] = newNodeI(nkPragma, info)
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
if kind == attachedDestructor:
|
||||
ensureMutable result
|
||||
incl result.optionsImpl, optQuirky
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
@@ -1231,7 +1257,9 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
result.ast[bodyPos].add v
|
||||
let placeHolder = newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
fillBody(a, typ, result.ast[bodyPos], d, placeHolder)
|
||||
if not a.canRaise: incl result.flags, sfNeverRaises
|
||||
if not a.canRaise:
|
||||
ensureMutable result
|
||||
incl result.flagsImpl, sfNeverRaises
|
||||
|
||||
|
||||
template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||
@@ -1266,7 +1294,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrival*(s: PSym): bool {.inline.} =
|
||||
proc isTrivial*(s: PSym): bool {.inline.} =
|
||||
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
|
||||
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
@@ -1275,11 +1303,13 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
## to ensure we lift assignment, destructors and moves properly.
|
||||
## The later 'injectdestructors' pass depends on it.
|
||||
if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
|
||||
incl orig.flags, tfCheckedForDestructor
|
||||
# IC: review this solution again later
|
||||
incl orig.flagsImpl, tfCheckedForDestructor
|
||||
# for user defined generic destructors:
|
||||
let origRoot = genericRoot(orig)
|
||||
if origRoot != nil:
|
||||
incl origRoot.flags, tfGenericHasDestructor
|
||||
# IC: review this solution again later
|
||||
incl origRoot.flagsImpl, tfGenericHasDestructor
|
||||
|
||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||
@@ -1305,7 +1335,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
# bug #15122: We need to produce all prototypes before entering the
|
||||
# mind boggling recursion. Hacks like these imply we should rewrite
|
||||
# this module.
|
||||
var generics: array[attachedWasMoved..attachedTrace, bool] = default(array[attachedWasMoved..attachedTrace, bool])
|
||||
var generics = default(array[attachedWasMoved..attachedTrace, bool])
|
||||
for k in attachedWasMoved..lastAttached:
|
||||
generics[k] = getAttachedOp(g, canon, k) != nil
|
||||
if not generics[k]:
|
||||
@@ -1321,8 +1351,9 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
if canon != orig:
|
||||
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
|
||||
|
||||
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrival(orig.assignment) or
|
||||
# not isTrival(orig.sink):
|
||||
orig.flags.incl tfHasAsgn
|
||||
if not isTrivial(getAttachedOp(g, orig, attachedDestructor)):
|
||||
#or not isTrivial(orig.assignment) or
|
||||
# not isTrivial(orig.sink):
|
||||
# IC: review this solution again later
|
||||
orig.flagsImpl.incl tfHasAsgn
|
||||
# ^ XXX Breaks IC!
|
||||
|
||||
@@ -110,6 +110,7 @@ type
|
||||
hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
|
||||
hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintUnknownRaises = "UnknownRaises",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
|
||||
@@ -236,6 +237,7 @@ const
|
||||
hintUser: "$1",
|
||||
hintUserRaw: "$1",
|
||||
hintExtendedContext: "$1",
|
||||
hintUnknownRaises: "$1 is a forward declaration without explicit .raises, assuming it can raise anything",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1"
|
||||
]
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
pathutils
|
||||
|
||||
import std/strutils
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
@@ -86,6 +86,47 @@ const
|
||||
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
|
||||
'|', '%', '&', '$', '@', '~', ','}
|
||||
AdditionalLineContinuationOprs = {'#', ':', '='}
|
||||
LineContinuationTokens = [
|
||||
"let", "var", "const", "type", # section
|
||||
"object", "tuple",
|
||||
# from ./layouter.oprSet
|
||||
"div", "mod", "shl", "shr", "in", "notin", "is",
|
||||
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
|
||||
] # must be all `nimIdentNormalized`-ed
|
||||
|
||||
proc eqIdent(a, bNormalized: string): bool =
|
||||
a.nimIdentNormalize == bNormalized
|
||||
|
||||
proc endsWithIdent(s, subs: string): bool =
|
||||
let le = subs.len
|
||||
if le > s.len: return false
|
||||
s[^le .. ^1].eqIdent subs
|
||||
|
||||
proc continuesWithIdent(s, subs: string, start: int): bool =
|
||||
s.substr(start, start+subs.high).eqIdent subs
|
||||
|
||||
proc endsWithIdent(s, subs: string, endIdx: var int): bool =
|
||||
endIdx.dec subs.len
|
||||
result = s.continuesWithIdent(subs, endIdx+1)
|
||||
|
||||
proc containsObjectOf(x: string): bool =
|
||||
const sep = ' '
|
||||
var idx = x.rfind(sep)
|
||||
if idx == -1: return
|
||||
template eatWord(word) =
|
||||
while x[idx] == sep: idx.dec
|
||||
result = x.endsWithIdent(word, idx)
|
||||
if not result: return
|
||||
eatWord "of"
|
||||
eatWord "object"
|
||||
result = true
|
||||
|
||||
proc endsWithLineContinuationToken(x: string): bool =
|
||||
result = false
|
||||
for tok in LineContinuationTokens:
|
||||
if x.endsWithIdent(tok):
|
||||
return true
|
||||
result = x.containsObjectOf
|
||||
|
||||
proc endsWithOpr*(x: string): bool =
|
||||
result = x.endsWith(LineContinuationOprs)
|
||||
@@ -93,7 +134,9 @@ proc endsWithOpr*(x: string): bool =
|
||||
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
|
||||
result = inTripleString or line.len > 0 and (
|
||||
line[0] == ' ' or
|
||||
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
|
||||
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
|
||||
line.endsWithLineContinuationToken()
|
||||
)
|
||||
|
||||
proc countTriples(s: string): int =
|
||||
result = 0
|
||||
@@ -109,7 +152,10 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
|
||||
s.rd = 0
|
||||
var line = newStringOfCap(120)
|
||||
var triples = 0
|
||||
while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
||||
while true:
|
||||
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
||||
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
|
||||
quit()
|
||||
s.s.add(line)
|
||||
s.s.add("\n")
|
||||
inc triples, countTriples(line)
|
||||
|
||||
@@ -58,13 +58,11 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
|
||||
else: handleError(n, origin)
|
||||
result = getIdent(c.cache, id)
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
if n[0].kind == nkSym:
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkOpenSym:
|
||||
result = considerQuotedIdent(c, n[0], origin)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
|
||||
@@ -77,10 +75,13 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym =
|
||||
proc openScope*(c: PContext): PScope {.discardable.} =
|
||||
result = PScope(parent: c.currentScope,
|
||||
symbols: initStrTable(),
|
||||
depthLevel: c.scopeDepth + 1)
|
||||
depthLevel: c.scopeDepth + 1,
|
||||
optionStackLen: c.optionStack.len)
|
||||
c.currentScope = result
|
||||
|
||||
proc rawCloseScope*(c: PContext) =
|
||||
if c.currentScope.optionStackLen >= 1:
|
||||
c.optionStack.setLen(c.currentScope.optionStackLen)
|
||||
c.currentScope = c.currentScope.parent
|
||||
|
||||
proc closeScope*(c: PContext) =
|
||||
@@ -310,7 +311,7 @@ proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
incl(result.flagsImpl, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
@@ -387,7 +388,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
||||
if sym.name.id == ord(wUnderscore): return
|
||||
let conflict = scope.addUniqueSym(sym)
|
||||
if conflict != nil:
|
||||
if sym.kind == skModule and conflict.kind == skModule:
|
||||
if sym.kind == skModule and conflict.kind == skModule and not c.config.isDefined("nimPreviewDuplicateModuleError"):
|
||||
# e.g.: import foo; import foo
|
||||
# xxx we could refine this by issuing a different hint for the case
|
||||
# where a duplicate import happens inside an include.
|
||||
|
||||
@@ -82,7 +82,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
|
||||
owner, value.info, g.config.options)
|
||||
temp.typ = skipTypes(value.typ, abstractInst)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flagsImpl, sfFromGeneric)
|
||||
tempAsNode = newSymNode(temp)
|
||||
|
||||
var v = newNodeI(nkVarSection, value.info)
|
||||
@@ -103,7 +103,7 @@ proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): P
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
|
||||
owner, value.info, g.config.options)
|
||||
temp.typ = skipTypes(value.typ, abstractInst)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flagsImpl, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
@@ -127,8 +127,8 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod
|
||||
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
|
||||
var temp = newSym(skVar, getIdent(g.cache, genPrefix), idgen, owner, n.info, owner.options)
|
||||
temp.typ = n[1].typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flags, sfGenSym)
|
||||
incl(temp.flagsImpl, sfFromGeneric)
|
||||
incl(temp.flagsImpl, sfGenSym)
|
||||
|
||||
var v = newNodeI(nkVarSection, n.info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
@@ -147,13 +147,13 @@ proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo
|
||||
result = newType(tyObject, idgen, owner)
|
||||
if final:
|
||||
rawAddSon(result, nil)
|
||||
incl result.flags, tfFinal
|
||||
incl result, tfFinal
|
||||
else:
|
||||
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
|
||||
result.n = newNodeI(nkRecList, info)
|
||||
let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
|
||||
idgen, owner, info, owner.options)
|
||||
incl s.flags, sfAnon
|
||||
incl s.flagsImpl, sfAnon
|
||||
s.typ = result
|
||||
result.sym = s
|
||||
|
||||
|
||||
@@ -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.skipTypes({tySink}), idgen)
|
||||
|
||||
@@ -117,6 +117,22 @@ when not defined(leanCompiler):
|
||||
else: raiseAssert $ext
|
||||
compilePipelineProject(graph)
|
||||
|
||||
proc commandCompileToNif(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
if conf.symbolFiles == disabledSf:
|
||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
||||
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
|
||||
# nothing changed
|
||||
graph.config.notes = graph.config.mainPackageNotes
|
||||
return
|
||||
|
||||
if not extccomp.ccHasSaneOverflow(conf):
|
||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||
|
||||
setPipeLinePass(graph, NifgenPass)
|
||||
compilePipelineProject(graph)
|
||||
|
||||
proc commandCompileToC(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
@@ -184,7 +200,7 @@ proc commandInteractive(graph: ModuleGraph) =
|
||||
discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
|
||||
else:
|
||||
var m = graph.makeStdinModule()
|
||||
incl(m.flags, sfMainModule)
|
||||
incl(m, sfMainModule)
|
||||
var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
|
||||
discard processPipelineModule(graph, m, idgen, s)
|
||||
@@ -257,7 +273,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
if conf.exc == excNone: conf.exc = excSetjmp
|
||||
of backendCpp:
|
||||
if conf.exc == excNone: conf.exc = excCpp
|
||||
of backendObjc: discard
|
||||
of backendObjc, backendNif: discard
|
||||
of backendJs:
|
||||
if conf.hcrOn:
|
||||
# XXX: At the moment, system.nim cannot be compiled in JS mode
|
||||
@@ -275,6 +291,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of backendCpp: commandCompileToC(graph)
|
||||
of backendObjc: commandCompileToC(graph)
|
||||
of backendJs: commandCompileToJS(graph)
|
||||
of backendNif: commandCompileToNif(graph)
|
||||
of backendInvalid: raiseAssert "unreachable"
|
||||
|
||||
template docLikeCmd(body) =
|
||||
|
||||
@@ -60,6 +60,7 @@ type
|
||||
SemPass
|
||||
JSgenPass
|
||||
CgenPass
|
||||
NifgenPass
|
||||
EvalPass
|
||||
InterpreterPass
|
||||
GenDependPass
|
||||
@@ -136,6 +137,8 @@ type
|
||||
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
procGlobals*: seq[PNode]
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
PPassContext* = ref TPassContext
|
||||
@@ -454,10 +457,10 @@ template getPContext(): untyped =
|
||||
else: c.c
|
||||
|
||||
when defined(nimsuggest):
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
else:
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onDef*(info: TLineInfo; s: PSym) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
|
||||
@@ -583,6 +586,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result.config = config
|
||||
result.cache = cache
|
||||
initModuleGraphFields(result)
|
||||
ast.setupProgram(config, cache)
|
||||
|
||||
proc resetAllModules*(g: ModuleGraph) =
|
||||
g.packageSyms = initStrTable()
|
||||
@@ -678,13 +682,13 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||
if m != nil:
|
||||
g.suggestSymbols.del(fileIdx)
|
||||
g.suggestErrors.del(fileIdx)
|
||||
incl m.flags, sfDirty
|
||||
incl m.flagsImpl, sfDirty
|
||||
|
||||
proc unmarkAllDirty*(g: ModuleGraph) =
|
||||
for i in 0i32..<g.ifaces.len.int32:
|
||||
let m = g.ifaces[i].module
|
||||
if m != nil:
|
||||
m.flags.excl sfDirty
|
||||
m.flagsImpl.excl sfDirty
|
||||
|
||||
proc isDirty*(g: ModuleGraph; m: PSym): bool =
|
||||
result = g.suggestMode and sfDirty in m.flags
|
||||
@@ -761,7 +765,7 @@ proc getPackage*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
result = pkgSym
|
||||
graph.packageSyms.strTableAdd(pkgSym)
|
||||
|
||||
func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
|
||||
proc belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
|
||||
## Check if symbol belongs to the 'stdlib' package.
|
||||
sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
|
||||
|
||||
|
||||
@@ -79,6 +79,10 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
|
||||
else:
|
||||
result = fileInfoIdx(conf, fullPath)
|
||||
|
||||
type
|
||||
SelectedBase = enum
|
||||
FromProject, FromSearchPath, FromNimblePath
|
||||
|
||||
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
## Mangle a relative module path to avoid path and symbol collisions.
|
||||
##
|
||||
@@ -87,9 +91,27 @@ proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
##
|
||||
## Example:
|
||||
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
|
||||
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
|
||||
var best = relativeTo(path, conf.projectPath).string
|
||||
var selectedBase = FromProject
|
||||
for x in conf.searchPaths:
|
||||
let other = relativeTo(path, x).string
|
||||
if other.len < best.len:
|
||||
best = other
|
||||
selectedBase = FromSearchPath
|
||||
for x in conf.nimblePaths:
|
||||
let other = relativeTo(path, x).string
|
||||
if other.len < best.len:
|
||||
best = other
|
||||
selectedBase = FromNimblePath
|
||||
let prefix =
|
||||
case selectedBase
|
||||
of FromProject: "@m"
|
||||
of FromSearchPath: "@p"
|
||||
of FromNimblePath: "@n"
|
||||
|
||||
prefix & best.multiReplace(
|
||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
|
||||
proc demangleModuleName*(path: string): string =
|
||||
## Demangle a relative module path.
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
|
||||
|
||||
@@ -32,9 +32,9 @@ proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
||||
# mechanism, which we do in order to assign each module a persistent ID.
|
||||
result = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
|
||||
result = PSym(kindImpl: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32),
|
||||
name: getModuleIdent(graph, filename),
|
||||
info: newLineInfo(fileIdx, 1, 1))
|
||||
infoImpl: newLineInfo(fileIdx, 1, 1))
|
||||
if not isNimIdentifier(result.name.s):
|
||||
rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s &
|
||||
"'; a module name must be a valid Nim identifier.")
|
||||
|
||||
@@ -133,6 +133,16 @@ proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
|
||||
var dummy: bool = false
|
||||
fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
|
||||
|
||||
proc registerNifSuffix*(conf: ConfigRef; suffix: string; isKnownFile: var bool): FileIndex =
|
||||
result = conf.m.filenameToIndexTbl.getOrDefault(suffix, InvalidFileIdx)
|
||||
if result == InvalidFileIdx:
|
||||
isKnownFile = false
|
||||
result = conf.m.fileInfos.len.FileIndex
|
||||
conf.m.fileInfos.add(newFileInfo(AbsoluteFile suffix, RelativeFile suffix))
|
||||
conf.m.filenameToIndexTbl[suffix] = result
|
||||
else:
|
||||
isKnownFile = true
|
||||
|
||||
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
||||
result = TLineInfo(fileIndex: fileInfoIdx)
|
||||
if line < int high(uint16):
|
||||
|
||||
1690
compiler/nifgen.nim
Normal file
1690
compiler/nifgen.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,9 @@ define:nimPreviewRangeDefault
|
||||
define:nimPreviewNonVarDestructor
|
||||
define:nimPreviewCheckedClose
|
||||
define:nimPreviewAsmSemSymbol
|
||||
define:nimPreviewCStringComparisons
|
||||
define:nimPreviewDuplicateModuleError
|
||||
|
||||
threads:off
|
||||
|
||||
#import:"$projectpath/testability"
|
||||
|
||||
@@ -207,7 +207,6 @@ proc parseAssignment(L: var Lexer, tok: var Token;
|
||||
checkSymbol(L, tok)
|
||||
val.add($tok)
|
||||
confTok(L, tok, config, condStack)
|
||||
config.currentConfigDir = parentDir(filename.string)
|
||||
if percent:
|
||||
processSwitch(s, strtabs.`%`(val, config.configVars,
|
||||
{useEnvironment, useEmpty}), passPP, info, config)
|
||||
@@ -249,6 +248,8 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
|
||||
setDefaultLibpath(conf)
|
||||
template readConfigFile(path) =
|
||||
let configPath = path
|
||||
conf.currentConfigDir = configPath.splitFile.dir.string
|
||||
setConfigVar(conf, "selfDir", conf.currentConfigDir)
|
||||
if readConfigFile(configPath, cache, conf):
|
||||
conf.configFiles.add(configPath)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ proc createInterpreter*(scriptName: string;
|
||||
if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
|
||||
|
||||
var m = graph.makeModule(scriptName)
|
||||
incl(m.flags, sfMainModule)
|
||||
incl(m, sfMainModule)
|
||||
var idgen = idGeneratorFromModule(m)
|
||||
var vm = newCtx(m, cache, graph, idgen)
|
||||
vm.mode = emRepl
|
||||
@@ -168,7 +168,7 @@ proc runRepl*(r: TLLRepl;
|
||||
if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
|
||||
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
||||
var m = graph.makeStdinModule()
|
||||
incl(m.flags, sfMainModule)
|
||||
incl(m, sfMainModule)
|
||||
var idgen = idGeneratorFromModule(m)
|
||||
|
||||
if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
|
||||
|
||||
@@ -28,11 +28,14 @@ type
|
||||
hasReturn, hasBreak: bool
|
||||
label: PSym # can be nil
|
||||
parent: ptr BasicBlock
|
||||
symToDel: seq[PNode]
|
||||
|
||||
Con = object
|
||||
somethingTodo: bool
|
||||
inFinally: int
|
||||
|
||||
proc invalidateWasMoved(c: var BasicBlock; x: PNode)
|
||||
|
||||
proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock =
|
||||
BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false,
|
||||
label: nil, parent: addr(parent))
|
||||
@@ -62,6 +65,10 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
|
||||
if this.hasReturn:
|
||||
parent.wasMovedLocs.setLen 0
|
||||
parent.hasReturn = true
|
||||
elif this.symToDel.len > 0:
|
||||
parent.symToDel = this.symToDel
|
||||
for i in this.symToDel:
|
||||
invalidateWasMoved(parent, i)
|
||||
|
||||
proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
|
||||
result = false
|
||||
@@ -149,6 +156,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
|
||||
# any usage of the location before destruction implies we
|
||||
# cannot elide the 'wasMoved(x)':
|
||||
b.invalidateWasMoved n
|
||||
b.symToDel.add n
|
||||
|
||||
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
|
||||
@@ -110,6 +110,8 @@ type # please make sure we have under 32 options
|
||||
optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types.
|
||||
optShowNonExportedFields # for documentation: show fields that are not exported
|
||||
optJsBigInt64 # use bigints for 64-bit integers in JS
|
||||
optItaniumMangle # mangling follows the Itanium spec
|
||||
optCompress # turn on AST compression by converting it to NIF
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
@@ -139,6 +141,7 @@ type
|
||||
backendCpp = "cpp"
|
||||
backendJs = "js"
|
||||
backendObjc = "objc"
|
||||
backendNif = "nif"
|
||||
# backendNimscript = "nimscript" # this could actually work
|
||||
# backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
|
||||
|
||||
@@ -171,10 +174,11 @@ type
|
||||
cmdNop
|
||||
cmdJsonscript # compile a .json build file
|
||||
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
|
||||
cmdCompileToNif
|
||||
|
||||
const
|
||||
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
|
||||
cmdCompileToJS, cmdCrun}
|
||||
cmdCompileToJS, cmdCrun, cmdCompileToNif}
|
||||
cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
|
||||
cmdCtags, cmdBuildindex}
|
||||
|
||||
@@ -222,7 +226,7 @@ type
|
||||
strictEffects,
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictDefs, # deadcode
|
||||
strictCaseObjects,
|
||||
inferGenericTypes,
|
||||
openSym, # remove nfDisabledOpenSym when this is default
|
||||
@@ -248,6 +252,8 @@ type
|
||||
## Useful for libraries that rely on local passC
|
||||
jsNoLambdaLifting
|
||||
## Old transformation for closures in JS backend
|
||||
noPanicOnExcept
|
||||
## don't panic on bare except
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
@@ -402,6 +408,7 @@ type
|
||||
projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull*: AbsoluteFile # projectPath/projectName
|
||||
projectIsStdin*: bool # whether we're compiling from stdin
|
||||
stdinFile*: AbsoluteFile # Filename to use in messages for stdin
|
||||
lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.'
|
||||
projectMainIdx*: FileIndex # the canonical path id of the main module
|
||||
projectMainIdx2*: FileIndex # consider merging with projectMainIdx
|
||||
@@ -578,6 +585,7 @@ proc newConfigRef*(): ConfigRef =
|
||||
projectPath: AbsoluteDir"", # holds a path like /home/alice/projects/nim/compiler/
|
||||
projectFull: AbsoluteFile"", # projectPath/projectName
|
||||
projectIsStdin: false, # whether we're compiling from stdin
|
||||
stdinFile: AbsoluteFile"stdinfile",
|
||||
projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
|
||||
command: "", # the main command (e.g. cc, check, scan, etc)
|
||||
commandArgs: @[], # any arguments after the main command
|
||||
|
||||
@@ -33,7 +33,7 @@ proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
|
||||
pkgIdent = getIdent(cache, pkgName)
|
||||
newSym(skPackage, pkgIdent, idGeneratorForPackage(int32(fileIdx)), nil, info)
|
||||
|
||||
func getPackageSymbol*(sym: PSym): PSym =
|
||||
proc getPackageSymbol*(sym: PSym): PSym =
|
||||
## Return the owning package symbol.
|
||||
assert sym != nil
|
||||
result = sym
|
||||
@@ -41,18 +41,18 @@ func getPackageSymbol*(sym: PSym): PSym =
|
||||
result = result.owner
|
||||
assert result != nil, repr(sym.info)
|
||||
|
||||
func getPackageId*(sym: PSym): int =
|
||||
proc getPackageId*(sym: PSym): int =
|
||||
## Return the owning package ID.
|
||||
sym.getPackageSymbol.id
|
||||
|
||||
func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
proc belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
conf.mainPackageId == sym.getPackageId
|
||||
|
||||
func belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
|
||||
proc belongsToProjectPackageMaybeNil*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
## Returns `false` if `sym` is nil.
|
||||
##
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import ast, types, msgs, idents, renderer, wordrecg, trees,
|
||||
options
|
||||
|
||||
import std/strutils
|
||||
import std/[strutils, assertions]
|
||||
|
||||
# we precompile the pattern here for efficiency into some internal
|
||||
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
|
||||
@@ -216,6 +216,11 @@ proc exprRoot*(n: PNode; allowCalls = true): PSym =
|
||||
else:
|
||||
break
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult
|
||||
|
||||
proc isLentableBranch(owner: PSym, n: PNode): bool =
|
||||
result = isAssignable(owner, n) in {arLentValue, arAddressableConst, arLentValue}
|
||||
|
||||
proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
## 'owner' can be nil!
|
||||
result = arNone
|
||||
@@ -308,6 +313,35 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
|
||||
# nkVarTy denotes an lvalue, but the example above is the only
|
||||
# possible code which will get us here
|
||||
result = arLValue
|
||||
of nkIfExpr, nkIfStmt:
|
||||
# allow 'if' expressions to be lent if all branches are lentable
|
||||
for branch in n:
|
||||
if branch.len == 2:
|
||||
if not isLentableBranch(owner, branch[1]):
|
||||
return
|
||||
elif branch.len == 1:
|
||||
if not isLentableBranch(owner, branch[0]):
|
||||
return
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement in isAssignable"
|
||||
result = arLentValue
|
||||
of nkCaseStmt:
|
||||
# allow 'case' expressions to be lent if all branches are lentable
|
||||
for i in 1 ..< n.len:
|
||||
let branch = n[i]
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
if not isLentableBranch(owner, branch[^1]):
|
||||
return
|
||||
of nkElifBranch:
|
||||
if not isLentableBranch(owner, branch[1]):
|
||||
return
|
||||
of nkElse:
|
||||
if not isLentableBranch(owner, branch[0]):
|
||||
return
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in isAssignable"
|
||||
result = arLentValue
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
@@ -638,8 +638,9 @@ proc semiStmtList(p: var Parser, result: PNode) =
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkParRi:
|
||||
break
|
||||
elif not (sameInd(p) or realInd(p)):
|
||||
parMessage(p, errInvalidIndentation)
|
||||
# ignore indent:
|
||||
#elif not (sameOrNoInd(p) or realInd(p)):
|
||||
# parMessage(p, errInvalidIndentation)
|
||||
let a = complexOrSimpleStmt(p)
|
||||
if a.kind == nkEmpty:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
@@ -699,10 +700,12 @@ proc parsePar(p: var Parser): PNode =
|
||||
asgn.add b
|
||||
result.add(asgn)
|
||||
if p.tok.tokType == tkSemiColon:
|
||||
getTok(p)
|
||||
semiStmtList(p, result)
|
||||
elif p.tok.tokType == tkSemiColon:
|
||||
# stmt context:
|
||||
result.add(a)
|
||||
getTok(p)
|
||||
semiStmtList(p, result)
|
||||
else:
|
||||
a = colonOrEquals(p, a)
|
||||
@@ -1153,7 +1156,10 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
|
||||
parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
|
||||
break
|
||||
else:
|
||||
parMessage(p, "expected closing ')'")
|
||||
if p.tok.tokType in tokKeywordLow..tokKeywordHigh:
|
||||
parMessage(p, errGenerated, "'" & $p.tok.ident.s & "' is a keyword and cannot be used as a parameter name")
|
||||
else:
|
||||
parMessage(p, "expected closing ')'")
|
||||
break
|
||||
result.add(a)
|
||||
if p.tok.tokType notin {tkComma, tkSemiColon}: break
|
||||
@@ -2107,12 +2113,28 @@ proc parseObjectCase(p: var Parser): PNode =
|
||||
#| objectBranches = objectBranch (IND{=} objectBranch)*
|
||||
#| (IND{=} 'elif' expr colcom objectPart)*
|
||||
#| (IND{=} 'else' colcom objectPart)?
|
||||
#| objectCase = 'case' declColonEquals ':'? COMMENT?
|
||||
#| objectCase = 'case' (declColonEquals / pragma)? ':'? COMMENT?
|
||||
#| (IND{>} objectBranches DED
|
||||
#| | IND{=} objectBranches)
|
||||
result = newNodeP(nkRecCase, p)
|
||||
getTokNoInd(p)
|
||||
var a = parseIdentColonEquals(p, {withPragma})
|
||||
getTok(p)
|
||||
if p.tok.tokType != tkOf:
|
||||
# of case will be handled later
|
||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
||||
var a: PNode
|
||||
if p.tok.tokType in {tkSymbol, tkAccent}:
|
||||
a = parseIdentColonEquals(p, {withPragma})
|
||||
else:
|
||||
a = newNodeP(nkIdentDefs, p)
|
||||
if p.tok.tokType == tkCurlyDotLe:
|
||||
var prag = newNodeP(nkPragmaExpr, p)
|
||||
prag.add(p.emptyNode)
|
||||
prag.add(parsePragma(p))
|
||||
a.add(prag)
|
||||
else:
|
||||
a.add(p.emptyNode)
|
||||
a.add(p.emptyNode)
|
||||
a.add(p.emptyNode)
|
||||
result.add(a)
|
||||
if p.tok.tokType == tkColon: getTok(p)
|
||||
flexComment(p, result)
|
||||
|
||||
@@ -173,7 +173,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fr
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
result.flags.incl flags
|
||||
result.incl flags
|
||||
registerModule(graph, result)
|
||||
processModuleAux("import")
|
||||
else:
|
||||
@@ -185,7 +185,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fr
|
||||
replayStateChanges(graph.packed.pm[m.int].module, graph)
|
||||
replayGenericCacheInformation(graph, m.int)
|
||||
elif graph.isDirty(result):
|
||||
result.flags.excl sfDirty
|
||||
result.excl sfDirty
|
||||
# reset module fields:
|
||||
initStrTables(graph, result)
|
||||
result.ast = nil
|
||||
|
||||
@@ -77,7 +77,7 @@ proc inSymChoice(sc, x: PNode): bool =
|
||||
result = false
|
||||
for i in 0..<sc.len:
|
||||
if sc[i].sym == x.sym: return true
|
||||
elif sc.kind == nkOpenSymChoice:
|
||||
elif sc.kind in {nkOpenSymChoice, nkOpenSym}:
|
||||
# same name suffices for open sym choices!
|
||||
result = sc[0].sym.name.id == x.sym.name.id
|
||||
else:
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
|
||||
lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
|
||||
packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
|
||||
liftdestructors
|
||||
liftdestructors, nifgen
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
import ast2nif
|
||||
|
||||
import pipelineutils
|
||||
|
||||
@@ -23,6 +26,10 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
|
||||
result = semNode
|
||||
if bModule != nil:
|
||||
genTopLevelStmt(BModule(bModule), result)
|
||||
of NifgenPass:
|
||||
result = semNode
|
||||
if bModule != nil:
|
||||
genTopLevelNif(bModule, result)
|
||||
of JSgenPass:
|
||||
when not defined(leanCompiler):
|
||||
result = processJSCodeGen(bModule, semNode)
|
||||
@@ -48,7 +55,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
|
||||
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) =
|
||||
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator;
|
||||
topLevelStmts: PNode) =
|
||||
# XXX fixme this should actually be relative to the config file!
|
||||
let relativeTo = toFullPath(graph.config, m.info)
|
||||
for module in items(implicits):
|
||||
@@ -60,8 +68,13 @@ proc processImplicitImports*(graph: ModuleGraph; implicits: seq[string], nodeKin
|
||||
importStmt.add str
|
||||
message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
|
||||
let semNode = semWithPContext(ctx, importStmt)
|
||||
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
|
||||
if semNode == nil:
|
||||
break
|
||||
let top = processPipeline(graph, semNode, bModule)
|
||||
if top == nil:
|
||||
break
|
||||
if topLevelStmts != nil:
|
||||
topLevelStmts.add top
|
||||
|
||||
proc prePass*(c: PContext; n: PNode) =
|
||||
for son in n:
|
||||
@@ -83,7 +96,7 @@ proc prePass*(c: PContext; n: PNode) =
|
||||
let feature = parseEnum[Feature](name.strVal)
|
||||
if feature == codeReordering:
|
||||
c.features.incl feature
|
||||
c.module.flags.incl sfReorder
|
||||
c.module.incl sfReorder
|
||||
except ValueError:
|
||||
discard
|
||||
else:
|
||||
@@ -131,6 +144,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
nil
|
||||
of SemPass:
|
||||
nil
|
||||
of NifgenPass:
|
||||
setupNifgen(graph, module, idgen)
|
||||
of NonePass:
|
||||
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
||||
|
||||
@@ -144,6 +159,11 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
else:
|
||||
s = stream
|
||||
graph.interactive = stream.kind == llsStdIn
|
||||
var topLevelStmts =
|
||||
if optCompress in graph.config.globalOptions:
|
||||
newNodeI(nkStmtList, module.info)
|
||||
else:
|
||||
nil
|
||||
while true:
|
||||
syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
|
||||
|
||||
@@ -153,8 +173,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
# in ROD files. I think we should enable this feature only
|
||||
# for the interactive mode.
|
||||
if module.name.s != "nimscriptapi":
|
||||
processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
|
||||
processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
|
||||
processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen, topLevelStmts
|
||||
processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen, topLevelStmts
|
||||
|
||||
checkFirstLineIndentation(p)
|
||||
block processCode:
|
||||
@@ -175,7 +195,9 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
if graph.pipelinePass != EvalPass:
|
||||
message(graph.config, sl.info, hintProcessingStmt, $idgen[])
|
||||
var semNode = semWithPContext(ctx, sl)
|
||||
discard processPipeline(graph, semNode, bModule)
|
||||
let top = processPipeline(graph, semNode, bModule)
|
||||
if top != nil and topLevelStmts != nil:
|
||||
topLevelStmts.add top
|
||||
|
||||
closeParser(p)
|
||||
if s.kind != llsStdIn: break
|
||||
@@ -207,9 +229,16 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
of Docgen2JsonPass:
|
||||
when not defined(leanCompiler):
|
||||
discard closeJson(graph, bModule, finalNode)
|
||||
of NifgenPass:
|
||||
closeNif(graph, bModule, finalNode)
|
||||
of NonePass:
|
||||
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
if optCompress in graph.config.globalOptions:
|
||||
topLevelStmts.add finalNode
|
||||
writeNifModule(graph.config, module.position.int32, topLevelStmts)
|
||||
|
||||
if graph.config.backend notin {backendC, backendCpp, backendObjc}:
|
||||
# We only write rod files here if no C-like backend is active.
|
||||
# The C-like backends have been patched to support the IC mechanism.
|
||||
@@ -234,18 +263,19 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
let filename = AbsoluteFile path
|
||||
if fileExists(filename): # it could be a stdinfile
|
||||
# it could be a stdinfile/cmdfile
|
||||
if fileExists(filename) and not graph.config.projectIsStdin:
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
result.flags.incl flags
|
||||
result.incl flags
|
||||
registerModule(graph, result)
|
||||
processModuleAux("import")
|
||||
else:
|
||||
if sfSystemModule in flags:
|
||||
graph.systemModule = result
|
||||
if sfMainModule in flags and graph.config.cmd == cmdM:
|
||||
result.flags.incl flags
|
||||
result.incl flags
|
||||
registerModule(graph, result)
|
||||
processModuleAux("import")
|
||||
partialInitModule(result, graph, fileIdx, filename)
|
||||
@@ -257,7 +287,7 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
replayStateChanges(graph.packed.pm[m.int].module, graph)
|
||||
replayGenericCacheInformation(graph, m.int)
|
||||
elif graph.isDirty(result):
|
||||
result.flags.excl sfDirty
|
||||
result.excl sfDirty
|
||||
# reset module fields:
|
||||
initStrTables(graph, result)
|
||||
result.ast = nil
|
||||
|
||||
@@ -210,8 +210,8 @@ type
|
||||
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
|
||||
cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips,
|
||||
cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430,
|
||||
cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32,
|
||||
cpuE2k, cpuLoongArch64
|
||||
cpuSparc64, cpuS390x, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64,
|
||||
cpuEsp, cpuWasm32, cpuE2k, cpuLoongArch64
|
||||
|
||||
type
|
||||
TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness,
|
||||
@@ -241,6 +241,7 @@ const
|
||||
(name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
|
||||
(name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
|
||||
(name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "s390x", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
|
||||
@@ -33,7 +33,7 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
|
||||
|
||||
let prc = newSym(skProc, n[3].ident, c.idgen, iter.sym.owner, iter.sym.info)
|
||||
prc.typ = copyType(iter.sym.typ, c.idgen, prc)
|
||||
excl prc.typ.flags, tfCapturesEnv
|
||||
excl prc.typ, tfCapturesEnv
|
||||
prc.typ.n.add newSymNode(getEnvParam(iter.sym))
|
||||
prc.typ.rawAddSon t
|
||||
let orig = iter.sym.ast
|
||||
|
||||
@@ -107,7 +107,7 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
||||
return it[1]
|
||||
|
||||
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool = false)
|
||||
isStatement: bool = false; comesFromPush = false)
|
||||
|
||||
proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
|
||||
var recorded = newNodeI(nkReplayAction, n.info)
|
||||
@@ -148,7 +148,7 @@ proc pragmaEnsures(c: PContext, n: PNode) =
|
||||
if o.kind in routineKinds and o.typ != nil and o.typ.returnType != nil:
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info)
|
||||
s.typ = o.typ.returnType
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
addDecl(c, s)
|
||||
n[1] = c.semExpr(c, n[1])
|
||||
closeScope(c)
|
||||
@@ -156,12 +156,12 @@ proc pragmaEnsures(c: PContext, n: PNode) =
|
||||
proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
# special cases to improve performance:
|
||||
if extname == "$1":
|
||||
s.loc.snippet = rope(s.name.s)
|
||||
s.setSnippet(rope(s.name.s))
|
||||
elif '$' notin extname:
|
||||
s.loc.snippet = rope(extname)
|
||||
s.setSnippet(rope(extname))
|
||||
else:
|
||||
try:
|
||||
s.loc.snippet = rope(extname % s.name.s)
|
||||
s.setSnippet(rope(extname % s.name.s))
|
||||
except ValueError:
|
||||
localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
|
||||
when hasFFI:
|
||||
@@ -170,36 +170,36 @@ proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
|
||||
proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
setExternName(c, s, extname, info)
|
||||
incl(s.flags, sfImportc)
|
||||
excl(s.flags, sfForward)
|
||||
s.incl(sfImportc)
|
||||
s.excl(sfForward)
|
||||
|
||||
proc makeExternExport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
setExternName(c, s, extname, info)
|
||||
incl(s.flags, sfExportc)
|
||||
s.incl(sfExportc)
|
||||
|
||||
proc processImportCompilerProc(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
setExternName(c, s, extname, info)
|
||||
incl(s.flags, sfImportc)
|
||||
excl(s.flags, sfForward)
|
||||
incl(s.loc.flags, lfImportCompilerProc)
|
||||
s.incl(sfImportc)
|
||||
s.excl(sfForward)
|
||||
incl(s.locImpl.flags, lfImportCompilerProc)
|
||||
|
||||
proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
setExternName(c, s, extname, info)
|
||||
incl(s.flags, sfImportc)
|
||||
incl(s.flags, sfInfixCall)
|
||||
excl(s.flags, sfForward)
|
||||
s.incl(sfImportc)
|
||||
incl(s.flagsImpl, sfInfixCall)
|
||||
excl(s.flagsImpl, sfForward)
|
||||
if c.config.backend == backendC:
|
||||
let m = s.getModule()
|
||||
incl(m.flags, sfCompileToCpp)
|
||||
incl(m.flagsImpl, sfCompileToCpp)
|
||||
incl c.config.globalOptions, optMixedMode
|
||||
|
||||
proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
setExternName(c, s, extname, info)
|
||||
incl(s.flags, sfImportc)
|
||||
incl(s.flags, sfNamedParamCall)
|
||||
excl(s.flags, sfForward)
|
||||
s.incl(sfImportc)
|
||||
incl(s.flagsImpl, sfNamedParamCall)
|
||||
excl(s.flagsImpl, sfForward)
|
||||
let m = s.getModule()
|
||||
incl(m.flags, sfCompileToObjc)
|
||||
m.incl(sfCompileToObjc)
|
||||
|
||||
proc newEmptyStrNode(c: PContext; n: PNode, strVal: string = ""): PNode {.noinline.} =
|
||||
result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
|
||||
@@ -239,14 +239,14 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
|
||||
proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) =
|
||||
s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1"))
|
||||
s.constraint.strVal = s.constraint.strVal % s.name.s
|
||||
s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp}
|
||||
s.flagsImpl.incl {flag, sfInfixCall, sfExportc, sfMangleCpp}
|
||||
|
||||
s.typ.callConv = ccMember
|
||||
incl c.config.globalOptions, optMixedMode
|
||||
|
||||
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
|
||||
sym.constraint = getStrLitNode(c, n)
|
||||
sym.flags.incl sfCodegenDecl
|
||||
sym.flagsImpl.incl sfCodegenDecl
|
||||
|
||||
proc processMagic(c: PContext, n: PNode, s: PSym) =
|
||||
#if sfSystemModule notin c.module.flags:
|
||||
@@ -282,10 +282,10 @@ proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) =
|
||||
|
||||
proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) =
|
||||
if isTurnedOn(c, n):
|
||||
incl(c.module.flags, flag)
|
||||
incl(c.module.flagsImpl, flag)
|
||||
c.features.incl codeReordering
|
||||
else:
|
||||
excl(c.module.flags, flag)
|
||||
excl(c.module.flagsImpl, flag)
|
||||
# c.features.excl codeReordering
|
||||
|
||||
# deprecated as of 0.18.1
|
||||
@@ -357,9 +357,9 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
|
||||
var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
|
||||
if not lib.isOverridden:
|
||||
addToLib(lib, sym)
|
||||
incl(sym.loc.flags, lfDynamicLib)
|
||||
sym.incl(lfDynamicLib)
|
||||
else:
|
||||
incl(sym.loc.flags, lfExportLib)
|
||||
sym.incl(lfExportLib)
|
||||
# since we'll be loading the dynlib symbols dynamically, we must use
|
||||
# a calling convention that doesn't introduce custom name mangling
|
||||
# cdecl is the default - the user can override this explicitly
|
||||
@@ -435,7 +435,7 @@ proc processExperimental(c: PContext; n: PNode) =
|
||||
if not isTopLevel(c):
|
||||
localError(c.config, n.info,
|
||||
"Code reordering experimental pragma only valid at toplevel")
|
||||
c.module.flags.incl sfReorder
|
||||
c.module.flagsImpl.incl sfReorder
|
||||
except ValueError:
|
||||
localError(c.config, n[1].info, "unknown experimental feature")
|
||||
else:
|
||||
@@ -636,7 +636,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||
var e = searchInScopes(con, getIdent(con.cache, sub), amb)
|
||||
# XXX what to do here if 'amb' is true?
|
||||
if e != nil:
|
||||
incl(e.flags, sfUsed)
|
||||
incl(e.flagsImpl, sfUsed)
|
||||
if isDefined(con.config, "nimPreviewAsmSemSymbol"):
|
||||
result.add con.semExprWithType(con, newSymNode(e), {efTypeAllowed})
|
||||
else:
|
||||
@@ -757,15 +757,15 @@ proc typeBorrow(c: PContext; sym: PSym, n: PNode) =
|
||||
let it = n[1]
|
||||
if it.kind != nkAccQuoted:
|
||||
localError(c.config, n.info, "a type can only borrow `.` for now")
|
||||
incl(sym.typ.flags, tfBorrowDot)
|
||||
incl(sym.typ, tfBorrowDot)
|
||||
|
||||
proc markCompilerProc(c: PContext; s: PSym) =
|
||||
# minor hack ahead: FlowVar is the only generic .compilerproc type which
|
||||
# should not have an external name set:
|
||||
if s.kind != skType or s.name.s != "FlowVar":
|
||||
makeExternExport(c, s, "$1", s.info)
|
||||
incl(s.flags, sfCompilerProc)
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s, sfCompilerProc)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
registerCompilerProc(c.graph, s)
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addCompilerProc(c.encoder, c.packedRepr, s)
|
||||
@@ -773,7 +773,7 @@ proc markCompilerProc(c: PContext; s: PSym) =
|
||||
proc deprecatedStmt(c: PContext; outerPragma: PNode) =
|
||||
let pragma = outerPragma[1]
|
||||
if pragma.kind in {nkStrLit..nkTripleStrLit}:
|
||||
incl(c.module.flags, sfDeprecated)
|
||||
incl(c.module, sfDeprecated)
|
||||
c.module.constraint = getStrLitNode(c, outerPragma)
|
||||
return
|
||||
if pragma.kind != nkBracket:
|
||||
@@ -842,7 +842,7 @@ proc processEffectsOf(c: PContext, n: PNode; owner: PSym) =
|
||||
let r = c.semExpr(c, n)
|
||||
if r.kind == nkSym and r.sym.kind == skParam:
|
||||
if r.sym.owner == owner:
|
||||
incl r.sym.flags, sfEffectsDelayed
|
||||
incl r.sym, sfEffectsDelayed
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated, "parameter cannot be declared as .effectsOf")
|
||||
else:
|
||||
@@ -893,7 +893,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if keyDeep:
|
||||
localError(c.config, it.info, "user pragma cannot have arguments")
|
||||
|
||||
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
|
||||
pragma(c, sym, userPragma.ast, validPragmas, isStatement, comesFromPush)
|
||||
n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
|
||||
i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
|
||||
else:
|
||||
@@ -907,8 +907,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if c.config.backend != backendCpp:
|
||||
localError(c.config, it.info, "exportcpp requires `cpp` backend, got: " & $c.config.backend)
|
||||
else:
|
||||
incl(sym.flags, sfMangleCpp)
|
||||
incl(sym.flags, sfUsed) # avoid wrong hints
|
||||
incl(sym, sfMangleCpp)
|
||||
incl(sym.flagsImpl, sfUsed) # avoid wrong hints
|
||||
of wImportc:
|
||||
let name = getOptionalStr(c, it, "$1")
|
||||
cppDefine(c.config, name)
|
||||
@@ -921,24 +921,24 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
processImportCompilerProc(c, sym, name, it.info)
|
||||
of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info)
|
||||
of wDirty:
|
||||
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
|
||||
if sym.kind == skTemplate: incl(sym, sfDirty)
|
||||
else: invalidPragma(c, it)
|
||||
of wRedefine:
|
||||
if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition)
|
||||
if sym.kind == skTemplate: incl(sym, sfTemplateRedefinition)
|
||||
else: invalidPragma(c, it)
|
||||
of wCallsite:
|
||||
if sym.kind == skTemplate: incl(sym.flags, sfCallsite)
|
||||
if sym.kind == skTemplate: incl(sym, sfCallsite)
|
||||
else: invalidPragma(c, it)
|
||||
of wImportCpp:
|
||||
processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
|
||||
of wCppNonPod:
|
||||
incl(sym.flags, sfCppNonPod)
|
||||
incl(sym, sfCppNonPod)
|
||||
of wImportJs:
|
||||
if c.config.backend != backendJs:
|
||||
localError(c.config, it.info, "`importjs` pragma requires the JavaScript target")
|
||||
let name = getOptionalStr(c, it, "$1")
|
||||
incl(sym.flags, sfImportc)
|
||||
incl(sym.flags, sfInfixCall)
|
||||
incl(sym, sfImportc)
|
||||
incl(sym.flagsImpl, sfInfixCall)
|
||||
if sym.kind in skProcKinds and {'(', '#', '@'} notin name:
|
||||
localError(c.config, n.info, "`importjs` for routines requires a pattern")
|
||||
setExternName(c, sym, name, it.info)
|
||||
@@ -947,15 +947,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wSize:
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
var size = expectIntLit(c, it)
|
||||
case size
|
||||
of 1, 2, 4:
|
||||
sym.typ.size = size
|
||||
sym.typ.align = int16 size
|
||||
of 8:
|
||||
sym.typ.size = 8
|
||||
sym.typ.align = floatInt64Align(c.config)
|
||||
if sfImportc in sym.flags:
|
||||
# no restrictions on size for imported types
|
||||
setImportedTypeSize(c.config, sym.typ, size)
|
||||
else:
|
||||
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
|
||||
case size
|
||||
of 1, 2, 4:
|
||||
sym.typ.size = size
|
||||
sym.typ.align = int16 size
|
||||
of 8:
|
||||
sym.typ.size = 8
|
||||
sym.typ.align = floatInt64Align(c.config)
|
||||
else:
|
||||
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
|
||||
of wAlign:
|
||||
let alignment = expectIntLit(c, it)
|
||||
if isPowerOfTwo(alignment) and alignment > 0:
|
||||
@@ -964,29 +968,29 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
localError(c.config, it.info, "power of two expected")
|
||||
of wNodecl:
|
||||
noVal(c, it)
|
||||
incl(sym.loc.flags, lfNoDecl)
|
||||
sym.incl(lfNoDecl)
|
||||
of wPure, wAsmNoStackFrame:
|
||||
noVal(c, it)
|
||||
if sym != nil:
|
||||
if k == wPure and sym.kind in routineKinds: invalidPragma(c, it)
|
||||
else: incl(sym.flags, sfPure)
|
||||
else: incl(sym, sfPure)
|
||||
of wVolatile:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfVolatile)
|
||||
incl(sym, sfVolatile)
|
||||
of wCursor:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfCursor)
|
||||
incl(sym, sfCursor)
|
||||
of wRegister:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfRegister)
|
||||
incl(sym, sfRegister)
|
||||
of wNoalias:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfNoalias)
|
||||
incl(sym, sfNoalias)
|
||||
of wEffectsOf:
|
||||
processEffectsOf(c, it, sym)
|
||||
of wThreadVar:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, {sfThread, sfGlobal})
|
||||
incl(sym, {sfThread, sfGlobal})
|
||||
of wDeadCodeElimUnused:
|
||||
warningDeprecated(c.config, n.info, "'{.deadcodeelim: on.}' is deprecated, now a noop") # deprecated, dead code elim always on
|
||||
of wNoForward: pragmaNoForward(c, it)
|
||||
@@ -996,51 +1000,50 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
noVal(c, it)
|
||||
if comesFromPush:
|
||||
if sym.kind in {skProc, skFunc}:
|
||||
incl(sym.flags, sfCompileTime)
|
||||
incl(sym, sfCompileTime)
|
||||
else:
|
||||
incl(sym.flags, sfCompileTime)
|
||||
incl(sym, sfCompileTime)
|
||||
#incl(sym.loc.flags, lfNoDecl)
|
||||
of wGlobal:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfGlobal)
|
||||
incl(sym.flags, sfPure)
|
||||
incl(sym, {sfGlobal, sfPure})
|
||||
of wConstructor:
|
||||
incl(sym.flags, sfConstructor)
|
||||
incl(sym, sfConstructor)
|
||||
if sfImportc notin sym.flags:
|
||||
sym.constraint = newEmptyStrNode(c, it, getOptionalStr(c, it, ""))
|
||||
sym.constraint.strVal = sym.constraint.strVal
|
||||
sym.flags.incl {sfExportc, sfMangleCpp}
|
||||
sym.flagsImpl.incl {sfExportc, sfMangleCpp}
|
||||
sym.typ.callConv = ccNoConvention
|
||||
of wHeader:
|
||||
var lib = getLib(c, libHeader, getStrLitNode(c, it))
|
||||
addToLib(lib, sym)
|
||||
incl(sym.flags, sfImportc)
|
||||
incl(sym.loc.flags, lfHeader)
|
||||
incl(sym.loc.flags, lfNoDecl)
|
||||
incl(sym, sfImportc)
|
||||
incl(sym.locImpl.flags, lfHeader)
|
||||
incl(sym.locImpl.flags, lfNoDecl)
|
||||
# implies nodecl, because otherwise header would not make sense
|
||||
if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
|
||||
if sym.locImpl.snippet == "": sym.locImpl.snippet = rope(sym.name.s)
|
||||
of wNoSideEffect:
|
||||
noVal(c, it)
|
||||
if sym != nil:
|
||||
incl(sym.flags, sfNoSideEffect)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
|
||||
incl(sym, sfNoSideEffect)
|
||||
if sym.typ != nil: incl(sym.typ, tfNoSideEffect)
|
||||
of wSideEffect:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfSideEffect)
|
||||
incl(sym, sfSideEffect)
|
||||
of wNoreturn:
|
||||
noVal(c, it)
|
||||
# Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
|
||||
if c.config.exc != excQuirky:
|
||||
incl(sym.flags, sfNoReturn)
|
||||
incl(sym, sfNoReturn)
|
||||
if sym.typ.returnType != nil:
|
||||
localError(c.config, sym.ast[paramsPos][0].info,
|
||||
".noreturn with return type not allowed")
|
||||
of wNoDestroy:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfGeneratedOp)
|
||||
incl(sym, sfGeneratedOp)
|
||||
of wNosinks:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfWasForwarded)
|
||||
incl(sym, sfWasForwarded)
|
||||
of wDynlib:
|
||||
processDynLib(c, it, sym)
|
||||
of wCompilerProc, wCore:
|
||||
@@ -1049,79 +1052,79 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
recordPragma(c, it, "cppdefine", sym.name.s)
|
||||
if sfFromGeneric notin sym.flags: markCompilerProc(c, sym)
|
||||
of wNonReloadable:
|
||||
sym.flags.incl sfNonReloadable
|
||||
sym.incl sfNonReloadable
|
||||
of wProcVar:
|
||||
# old procvar annotation, no longer needed
|
||||
noVal(c, it)
|
||||
of wExplain:
|
||||
sym.flags.incl sfExplain
|
||||
sym.incl sfExplain
|
||||
of wDeprecated:
|
||||
if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}:
|
||||
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
|
||||
incl(sym.flags, sfDeprecated)
|
||||
incl(sym, sfDeprecated)
|
||||
elif sym != nil and sym.kind != skModule:
|
||||
# We don't support the extra annotation field
|
||||
if it.kind in nkPragmaCallKinds:
|
||||
localError(c.config, it.info, "annotation to deprecated not supported here")
|
||||
incl(sym.flags, sfDeprecated)
|
||||
incl(sym, sfDeprecated)
|
||||
# At this point we're quite sure this is a statement and applies to the
|
||||
# whole module
|
||||
elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
|
||||
else: incl(c.module.flags, sfDeprecated)
|
||||
else: incl(c.module, sfDeprecated)
|
||||
of wVarargs:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfVarargs)
|
||||
else: incl(sym.typ, tfVarargs)
|
||||
of wBorrow:
|
||||
if sym.kind == skType:
|
||||
typeBorrow(c, sym, it)
|
||||
else:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfBorrow)
|
||||
incl(sym, sfBorrow)
|
||||
of wFinal:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfFinal)
|
||||
else: incl(sym.typ, tfFinal)
|
||||
of wInheritable:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfInheritable)
|
||||
else: incl(sym.typ, tfInheritable)
|
||||
of wPackage:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.flags, sfForward)
|
||||
else: incl(sym, sfForward)
|
||||
of wAcyclic:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfAcyclic)
|
||||
else: incl(sym.typ, tfAcyclic)
|
||||
of wShallow:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfShallow)
|
||||
else: incl(sym.typ, tfShallow)
|
||||
of wThread:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfThread)
|
||||
incl(sym, sfThread)
|
||||
if sym.typ != nil:
|
||||
incl(sym.typ.flags, tfThread)
|
||||
incl(sym.typ, tfThread)
|
||||
if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall
|
||||
of wSendable:
|
||||
noVal(c, it)
|
||||
if sym != nil and sym.typ != nil:
|
||||
incl(sym.typ.flags, tfSendable)
|
||||
incl(sym.typ, tfSendable)
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
of wGcSafe:
|
||||
noVal(c, it)
|
||||
if sym != nil:
|
||||
if sym.kind != skType: incl(sym.flags, sfThread)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
|
||||
if sym.kind != skType: incl(sym, sfThread)
|
||||
if sym.typ != nil: incl(sym.typ, tfGcSafe)
|
||||
else: invalidPragma(c, it)
|
||||
else:
|
||||
discard "no checking if used as a code block"
|
||||
of wPacked:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfPacked)
|
||||
else: incl(sym.typ, tfPacked)
|
||||
of wHint:
|
||||
let s = expectStrLit(c, it)
|
||||
recordPragma(c, it, "hint", s)
|
||||
@@ -1137,8 +1140,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
# distinguish properly between
|
||||
# ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
|
||||
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
|
||||
incl(sym.flags, sfError)
|
||||
excl(sym.flags, sfForward)
|
||||
incl(sym, sfError)
|
||||
excl(sym, sfForward)
|
||||
else:
|
||||
let s = expectStrLit(c, it)
|
||||
recordPragma(c, it, "error", s)
|
||||
@@ -1148,18 +1151,18 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wUndef: processUndef(c, it)
|
||||
of wCompile:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
incl(m.flagsImpl, sfUsed)
|
||||
processCompile(c, it)
|
||||
of wLink: processLink(c, it)
|
||||
of wPassl:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
incl(m.flagsImpl, sfUsed)
|
||||
let s = expectStrLit(c, it)
|
||||
extccomp.addLinkOption(c.config, s)
|
||||
recordPragma(c, it, "passl", s)
|
||||
of wPassc:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
incl(m.flagsImpl, sfUsed)
|
||||
let s = expectStrLit(c, it)
|
||||
extccomp.addCompileOption(c.config, s)
|
||||
recordPragma(c, it, "passc", s)
|
||||
@@ -1177,16 +1180,16 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
result = true
|
||||
of wPragma:
|
||||
if not sym.isNil and sym.kind == skTemplate:
|
||||
sym.flags.incl sfCustomPragma
|
||||
sym.incl sfCustomPragma
|
||||
else:
|
||||
processPragma(c, n, i)
|
||||
result = true
|
||||
of wDiscardable:
|
||||
noVal(c, it)
|
||||
if sym != nil: incl(sym.flags, sfDiscardable)
|
||||
if sym != nil: incl(sym, sfDiscardable)
|
||||
of wNoInit:
|
||||
noVal(c, it)
|
||||
if sym != nil: incl(sym.flags, sfNoInit)
|
||||
if sym != nil: incl(sym, sfNoInit)
|
||||
of wCodegenDecl: processCodegenDecl(c, it, sym)
|
||||
of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks,
|
||||
wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints,
|
||||
@@ -1196,7 +1199,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
processOption(c, it, c.config.options)
|
||||
of wStackTrace, wLineTrace:
|
||||
if sym.kind in {skProc, skMethod, skConverter}:
|
||||
processOption(c, it, sym.options)
|
||||
ensureMutable sym
|
||||
processOption(c, it, sym.optionsImpl)
|
||||
else:
|
||||
processOption(c, it, c.config.options)
|
||||
of FirstCallConv..LastCallConv:
|
||||
@@ -1204,7 +1208,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else:
|
||||
sym.typ.callConv = wordToCallConv(k)
|
||||
sym.typ.flags.incl tfExplicitCallConv
|
||||
sym.typ.incl tfExplicitCallConv
|
||||
of wEmit: pragmaEmit(c, it)
|
||||
of wUnroll: pragmaUnroll(c, it)
|
||||
of wLinearScanEnd, wComputedGoto: noVal(c, it)
|
||||
@@ -1214,11 +1218,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wIncompleteStruct:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfIncompleteStruct)
|
||||
else: incl(sym.typ, tfIncompleteStruct)
|
||||
of wCompleteStruct:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfCompleteStruct)
|
||||
else: incl(sym.typ, tfCompleteStruct)
|
||||
of wUnchecked:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil or sym.typ.kind notin {tyArray, tyUncheckedArray}:
|
||||
@@ -1231,34 +1235,35 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else:
|
||||
noVal(c, it)
|
||||
if sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfUnion)
|
||||
else: incl(sym.typ, tfUnion)
|
||||
of wRequiresInit:
|
||||
noVal(c, it)
|
||||
if sym.kind == skField:
|
||||
sym.flags.incl sfRequiresInit
|
||||
sym.incl sfRequiresInit
|
||||
elif sym.typ != nil:
|
||||
incl(sym.typ.flags, tfNeedsFullInit)
|
||||
incl(sym.typ, tfNeedsFullInit)
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
of wByRef:
|
||||
noVal(c, it)
|
||||
if sym != nil and sym.kind == skParam:
|
||||
sym.options.incl optByRef
|
||||
ensureMutable sym
|
||||
sym.optionsImpl.incl optByRef
|
||||
elif sym == nil or sym.typ == nil:
|
||||
processOption(c, it, c.config.options)
|
||||
else:
|
||||
incl(sym.typ.flags, tfByRef)
|
||||
incl(sym.typ, tfByRef)
|
||||
of wByCopy:
|
||||
noVal(c, it)
|
||||
if sym.kind == skParam:
|
||||
incl(sym.flags, sfByCopy)
|
||||
incl(sym, sfByCopy)
|
||||
elif sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfByCopy)
|
||||
else: incl(sym.typ, tfByCopy)
|
||||
of wPartial:
|
||||
noVal(c, it)
|
||||
if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
|
||||
else:
|
||||
incl(sym.typ.flags, tfPartial)
|
||||
incl(sym.typ, tfPartial)
|
||||
of wInject, wGensym:
|
||||
# We check for errors, but do nothing with these pragmas otherwise
|
||||
# as they are handled directly in 'evalTemplate'.
|
||||
@@ -1286,7 +1291,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if sym == nil or sym.kind notin {skVar, skLet}:
|
||||
invalidPragma(c, it)
|
||||
else:
|
||||
sym.flags.incl sfGoto
|
||||
sym.incl sfGoto
|
||||
of wExportNims:
|
||||
if sym == nil: invalidPragma(c, it)
|
||||
else: magicsys.registerNimScriptSymbol(c.graph, sym)
|
||||
@@ -1301,7 +1306,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
noVal(c, it)
|
||||
of wBase:
|
||||
noVal(c, it)
|
||||
sym.flags.incl sfBase
|
||||
sym.incl sfBase
|
||||
of wIntDefine:
|
||||
processDefineConst(c, n, sym, mIntDefine)
|
||||
of wStrDefine:
|
||||
@@ -1311,17 +1316,22 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wUsed:
|
||||
noVal(c, it)
|
||||
if sym == nil: invalidPragma(c, it)
|
||||
else: sym.flags.incl sfUsed
|
||||
else: sym.incl sfUsed
|
||||
of wLiftLocals:
|
||||
sym.flags.incl(sfForceLift)
|
||||
sym.incl(sfForceLift)
|
||||
of wRequires, wInvariant, wAssume, wAssert:
|
||||
pragmaProposition(c, it)
|
||||
of wEnsures:
|
||||
pragmaEnsures(c, it)
|
||||
of wEnforceNoRaises, wQuirky:
|
||||
sym.flags.incl sfNeverRaises
|
||||
of wEnforceNoRaises:
|
||||
sym.incl sfNeverRaises
|
||||
of wQuirky:
|
||||
sym.incl sfNeverRaises
|
||||
if sym.kind in {skProc, skMethod, skConverter, skFunc, skIterator}:
|
||||
ensureMutable sym
|
||||
sym.optionsImpl.incl optQuirky
|
||||
of wSystemRaisesDefect:
|
||||
sym.flags.incl sfSystemRaisesDefect
|
||||
sym.incl sfSystemRaisesDefect
|
||||
of wVirtual:
|
||||
processVirtual(c, it, sym, sfVirtual)
|
||||
of wMember:
|
||||
@@ -1378,9 +1388,9 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
|
||||
var lib = c.optionStack[^1].dynlib
|
||||
if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
|
||||
sfImportc in sym.flags and lib != nil:
|
||||
incl(sym.loc.flags, lfDynamicLib)
|
||||
incl(sym, lfDynamicLib)
|
||||
addToLib(lib, sym)
|
||||
if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
|
||||
if sym.locImpl.snippet == "": sym.locImpl.snippet = rope(sym.name.s)
|
||||
|
||||
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
|
||||
if n == nil: return false
|
||||
@@ -1401,11 +1411,12 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
inc i
|
||||
|
||||
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool) =
|
||||
isStatement: bool; comesFromPush = false) =
|
||||
if n == nil: return
|
||||
pragmaRec(c, sym, n, validPragmas, isStatement)
|
||||
# XXX: in the case of a callable def, this should use its info
|
||||
implicitPragmas(c, sym, n.info, validPragmas)
|
||||
if not comesFromPush:
|
||||
implicitPragmas(c, sym, n.info, validPragmas)
|
||||
|
||||
proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords,
|
||||
isStatement: bool = false) =
|
||||
|
||||
@@ -30,7 +30,7 @@ type
|
||||
TRenderFlags* = set[TRenderFlag]
|
||||
TRenderTok* = object
|
||||
kind*: TokType
|
||||
length*: int16
|
||||
length*: int32
|
||||
sym*: PSym
|
||||
|
||||
Section = enum
|
||||
@@ -154,7 +154,7 @@ proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen =
|
||||
)
|
||||
|
||||
proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
||||
g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
|
||||
g.tokens.add TRenderTok(kind: kind, length: int32(s.len), sym: sym)
|
||||
g.buf.add(s)
|
||||
if kind != tkSpaces:
|
||||
inc g.col, s.len
|
||||
@@ -327,6 +327,10 @@ proc pushCom(g: var TSrcGen, n: PNode) =
|
||||
setLen(g.comStack, g.comStack.len + 1)
|
||||
g.comStack[^1] = n
|
||||
|
||||
proc popCom(g: var TSrcGen): PNode =
|
||||
result = g.comStack[^1]
|
||||
setLen(g.comStack, g.comStack.len - 1)
|
||||
|
||||
proc popAllComs(g: var TSrcGen) =
|
||||
setLen(g.comStack, 0)
|
||||
|
||||
@@ -410,7 +414,7 @@ proc atom(g: TSrcGen; n: PNode): string =
|
||||
of nkEmpty: result = ""
|
||||
of nkIdent: result = n.ident.s
|
||||
of nkSym: result = n.sym.name.s
|
||||
of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
|
||||
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym: result = n[0].sym.name.s
|
||||
of nkStrLit: result = ""; result.addQuoted(n.strVal)
|
||||
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
|
||||
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
|
||||
@@ -565,8 +569,16 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
of nkIfExpr:
|
||||
result = lsub(g, n[0][0]) + lsub(g, n[0][1]) + lsons(g, n, 1) +
|
||||
len("if_:_")
|
||||
of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
|
||||
of nkElseExpr: result = lsub(g, n[0]) + len("_else:_") # type descriptions
|
||||
of nkElifExpr, nkElifBranch:
|
||||
if isEmptyType(n[1].typ):
|
||||
result = lsons(g, n) + len("elif_:_")
|
||||
else:
|
||||
result = lsons(g, n) + len("_elif_:_")
|
||||
of nkElseExpr, nkElse:
|
||||
if isEmptyType(n[0].typ):
|
||||
result = lsub(g, n[0]) + len("else:_")
|
||||
else:
|
||||
result = lsub(g, n[0]) + len("_else:_") # type descriptions
|
||||
of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
|
||||
of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
|
||||
of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
|
||||
@@ -609,8 +621,6 @@ proc lsub(g: TSrcGen; n: PNode): int =
|
||||
of nkCommentStmt: result = n.comment.len
|
||||
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
|
||||
of nkImportAs: result = lsub(g, n[0]) + len("_as_") + lsub(g, n[1])
|
||||
of nkElifBranch: result = lsons(g, n) + len("elif_:_")
|
||||
of nkElse: result = lsub(g, n[0]) + len("else:_")
|
||||
of nkFinally: result = lsub(g, n[0]) + len("finally:_")
|
||||
of nkGenericParams: result = lcomma(g, n) + 2
|
||||
of nkFormalParams:
|
||||
@@ -1002,7 +1012,7 @@ type
|
||||
proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
|
||||
if renderIds notin g.flags:
|
||||
case n.kind
|
||||
of nkClosedSymChoice, nkOpenSymChoice:
|
||||
of nkClosedSymChoice, nkOpenSymChoice, nkOpenSym:
|
||||
if n.len > 0: result = bracketKind(g, n[0])
|
||||
else: result = bkNone
|
||||
of nkSym:
|
||||
@@ -1347,6 +1357,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
if not n[0].isExported() and renderNonExportedFields notin g.flags:
|
||||
# Skip if this is a property in a type and its not exported
|
||||
# (While also not allowing rendering of non exported fields)
|
||||
if shouldRenderComment(g, n):
|
||||
# `shouldRenderComment` indicts that we have comments to render
|
||||
# but it's a non-exported field, so we just pop without rendering any comment
|
||||
discard popCom(g)
|
||||
return
|
||||
# render postfix for object fields:
|
||||
exclFlags = g.flags * {renderNoPostfix}
|
||||
@@ -1415,10 +1429,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkPrefix:
|
||||
gsub(g, n, 0)
|
||||
if n.len > 1:
|
||||
let opr = if n[0].kind == nkIdent: n[0].ident
|
||||
elif n[0].kind == nkSym: n[0].sym.name
|
||||
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
||||
else: nil
|
||||
let opr = getPIdent(n[0])
|
||||
let nNext = skipHiddenNodes(n[1])
|
||||
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||
put(g, tkSpaces, Space)
|
||||
@@ -1469,15 +1480,30 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
putWithSpace(g, tkColon, ":")
|
||||
if n.len > 0: gsub(g, n[0], 1)
|
||||
gsons(g, n, emptyContext, 1)
|
||||
of nkElifExpr:
|
||||
putWithSpace(g, tkElif, " elif")
|
||||
gcond(g, n[0])
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 1)
|
||||
of nkElseExpr:
|
||||
put(g, tkElse, " else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 0)
|
||||
of nkElifExpr, nkElifBranch:
|
||||
if isEmptyType(n[1].typ):
|
||||
optNL(g)
|
||||
putWithSpace(g, tkElif, "elif")
|
||||
gsub(g, n, 0)
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[1], c)
|
||||
else:
|
||||
putWithSpace(g, tkElif, " elif")
|
||||
gcond(g, n[0])
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 1)
|
||||
of nkElseExpr, nkElse:
|
||||
if isEmptyType(n[0].typ):
|
||||
optNL(g)
|
||||
put(g, tkElse, "else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[0], c)
|
||||
else:
|
||||
put(g, tkElse, " else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 0)
|
||||
of nkTypeOfExpr:
|
||||
put(g, tkType, "typeof")
|
||||
put(g, tkParLe, "(")
|
||||
@@ -1739,19 +1765,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkMixinStmt:
|
||||
putWithSpace(g, tkMixin, "mixin")
|
||||
gcomma(g, n, c)
|
||||
of nkElifBranch:
|
||||
optNL(g)
|
||||
putWithSpace(g, tkElif, "elif")
|
||||
gsub(g, n, 0)
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[1], c)
|
||||
of nkElse:
|
||||
optNL(g)
|
||||
put(g, tkElse, "else")
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gcoms(g)
|
||||
gstmts(g, n[0], c)
|
||||
of nkFinally, nkDefer:
|
||||
optNL(g)
|
||||
if n.kind == nkFinally:
|
||||
|
||||
@@ -93,7 +93,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
|
||||
of nkIdent: uses.incl n.ident.id
|
||||
of nkSym: uses.incl n.sym.name.id
|
||||
of nkAccQuoted: uses.incl accQuoted(cache, n).id
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
uses.incl n[0].sym.name.id
|
||||
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
|
||||
for i in 0..<n.len: computeDeps(cache, n[i], declares, uses, topLevel)
|
||||
|
||||
@@ -215,7 +215,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
conf.selectedGC = gcUnselected
|
||||
|
||||
var m = graph.makeModule(scriptName)
|
||||
incl(m.flags, sfMainModule)
|
||||
incl(m, sfMainModule)
|
||||
var vm = setupVM(m, cache, scriptName.string, graph, idgen)
|
||||
graph.vm = vm
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ proc fitNodeConsiderViewType(c: PContext, formal: PType, arg: PNode; info: TLine
|
||||
#classifyViewType(formal) != noView:
|
||||
result = newNodeIT(nkHiddenAddr, a.info, formal)
|
||||
result.add a
|
||||
formal.flags.incl tfVarIsPtr
|
||||
formal.incl tfVarIsPtr
|
||||
else:
|
||||
result = a
|
||||
|
||||
@@ -260,7 +260,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
if find(result.name.s, '`') >= 0:
|
||||
result.flags.incl sfWasGenSym
|
||||
result.flagsImpl.incl sfWasGenSym
|
||||
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||
# incl(result.flags, sfGlobal)
|
||||
when defined(nimsuggest):
|
||||
@@ -321,7 +321,7 @@ proc hasCycle(n: PNode): bool =
|
||||
break
|
||||
excl n.flags, nfNone
|
||||
|
||||
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
|
||||
proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: var bool): PNode =
|
||||
# recompute the types as 'eval' isn't guaranteed to construct types nor
|
||||
# that the types are sound:
|
||||
when true:
|
||||
@@ -333,7 +333,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
|
||||
if hasCycle(result):
|
||||
result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further")
|
||||
else:
|
||||
semmacrosanity.annotateType(result, expectedType, c.config)
|
||||
semmacrosanity.annotateType(result, expectedType, c.config, producedClosure)
|
||||
else:
|
||||
result = semExprWithType(c, evaluated)
|
||||
#result = fitNode(c, e.typ, result) inlined with special case:
|
||||
@@ -346,6 +346,19 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
|
||||
isArrayConstr(arg):
|
||||
arg.typ = eOrig.typ
|
||||
|
||||
proc resetEvalPosition(n: PNode) =
|
||||
# resets the eval position of variables because `tryConstExpr` may be
|
||||
# called multiple times on the same node
|
||||
case n.kind
|
||||
of {nkNone..nkNilLit}-{nkSym}:
|
||||
discard
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skLet} and sfGlobal notin n.sym.flags:
|
||||
n.sym.position = 0
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
resetEvalPosition(n[i])
|
||||
|
||||
proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
var e = semExprWithType(c, n, expectedType = expectedType)
|
||||
if e == nil: return
|
||||
@@ -370,7 +383,10 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
if result == nil or result.kind == nkEmpty:
|
||||
result = nil
|
||||
else:
|
||||
result = fixupTypeAfterEval(c, result, e)
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, e, producedClosure)
|
||||
if producedClosure:
|
||||
result = nil
|
||||
|
||||
except ERecoverableError:
|
||||
result = nil
|
||||
@@ -379,6 +395,8 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# Restore the error hook
|
||||
c.graph.config.structuredErrorHook = tempHook
|
||||
|
||||
resetEvalPosition(n)
|
||||
|
||||
c.config.errorCounter = oldErrorCount
|
||||
c.config.errorMax = oldErrorMax
|
||||
c.config.m.errorOutputs = oldErrorOutputs
|
||||
@@ -407,7 +425,10 @@ proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# error correction:
|
||||
result = e
|
||||
else:
|
||||
result = fixupTypeAfterEval(c, result, e)
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, e, producedClosure)
|
||||
if producedClosure:
|
||||
result = nil
|
||||
|
||||
proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
if efNeedStatic in flags:
|
||||
@@ -755,6 +776,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
|
||||
result.semTypeNode = semTypeNode
|
||||
result.instTypeBoundOp = sigmatch.instTypeBoundOp
|
||||
result.hasUnresolvedArgs = hasUnresolvedArgs
|
||||
result.semAsgnOpr = semAsgnOpr
|
||||
result.templInstCounter = new int
|
||||
|
||||
pushProcCon(result, module)
|
||||
|
||||
@@ -144,7 +144,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
while true:
|
||||
determineType(c, sym)
|
||||
z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag)
|
||||
|
||||
# this is kinda backwards as without a check here the described
|
||||
# problems in recalc would not happen, but instead it 100%
|
||||
# does check forever in some cases
|
||||
@@ -184,7 +183,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# 1) new symbols are discovered but the loop ends before we recalc
|
||||
# 2) new symbols are discovered and resemmed forever
|
||||
# not 100% sure if these are possible though as they would rely
|
||||
# on somehow introducing a new overload during overload resolution
|
||||
# on somehow introducing a new overload during overload resolution
|
||||
|
||||
# Symbol table has been modified. Restart and pre-calculate all syms
|
||||
# before any further candidate init and compare. SLOW, but rare case.
|
||||
@@ -245,14 +244,6 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) =
|
||||
if not c.graph.compatibleProps(c.graph, f, a):
|
||||
result.add "\n The `.requires` or `.ensures` properties are incompatible."
|
||||
|
||||
proc renderNotLValue(n: PNode): string =
|
||||
result = $n
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if n.kind == nkHiddenCallConv and n.len > 1:
|
||||
result = $n[0] & "(" & result & ")"
|
||||
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
|
||||
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
|
||||
|
||||
proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
(TPreferedDesc, string) =
|
||||
var prefer = preferName
|
||||
@@ -590,7 +581,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
let overloadsState = result.state
|
||||
if overloadsState != csMatch:
|
||||
if nfDotField in n.flags:
|
||||
internalAssert c.config, f.kind == nkIdent and n.len >= 2
|
||||
internalAssert c.config, f.kind in nkIdentKinds and n.len >= 2
|
||||
|
||||
# leave the op head symbol empty,
|
||||
# we are going to try multiple variants
|
||||
@@ -695,7 +686,14 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
||||
if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
|
||||
let s = a[0].sym
|
||||
if s.isGenericRoutineStrict:
|
||||
let finalCallee = generateInstance(c, s, x.bindings, a.info)
|
||||
var src = s.typ.firstParamType
|
||||
var convMatch = newCandidate(c, src)
|
||||
var arg = a[1]
|
||||
if arg.kind in {nkHiddenAddr, nkHiddenSubConv}: arg = arg[^1]
|
||||
let srca = typeRel(convMatch, src, arg.typ)
|
||||
if srca notin {isEqual, isGeneric, isSubtype}:
|
||||
internalError(c.config, a.info, "generic converter failed rematch")
|
||||
let finalCallee = generateInstance(c, s, convMatch.bindings, a.info)
|
||||
a[0].sym = finalCallee
|
||||
a[0].typ() = finalCallee.typ
|
||||
#a.typ = finalCallee.typ.returnType
|
||||
@@ -838,15 +836,18 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
assert x.state == csMatch
|
||||
var finalCallee = x.calleeSym
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, finalCallee)
|
||||
onUse(info, finalCallee)
|
||||
markUsed(c, info, finalCallee, isGenericInstance = false)
|
||||
onUse(info, finalCallee, isGenericInstance = false)
|
||||
assert finalCallee.ast != nil
|
||||
if x.matchedErrorType:
|
||||
markUsed(c, info, finalCallee, isGenericInstance = true)
|
||||
onUse(info, finalCallee, isGenericInstance = true)
|
||||
if x.matchedErrorType:
|
||||
result = x.call
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
if containsGenericType(result.typ):
|
||||
result.typ() = newTypeS(tyError, c)
|
||||
incl result.typ.flags, tfCheckedForDestructor
|
||||
incl result.typ, tfCheckedForDestructor
|
||||
return
|
||||
let gp = finalCallee.ast[genericParamsPos]
|
||||
if gp.isGenericParams:
|
||||
@@ -876,6 +877,8 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
x.call.add tn
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
markUsed(c, info, finalCallee, isGenericInstance = true)
|
||||
onUse(info, finalCallee, isGenericInstance = true)
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
@@ -911,15 +914,15 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
if c.inGenericContext > 0 and c.matchedConcept == nil:
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
elif efNoUndeclared in flags:
|
||||
result = nil
|
||||
elif efExplain notin flags:
|
||||
# repeat the overload resolution,
|
||||
# this time enabling all the diagnostic output (this should fail again)
|
||||
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
|
||||
elif efNoUndeclared notin flags:
|
||||
result = nil
|
||||
notFoundError(c, n, errors)
|
||||
else:
|
||||
result = nil
|
||||
notFoundError(c, n, errors)
|
||||
|
||||
proc explicitGenericInstError(c: PContext; n: PNode): PNode =
|
||||
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
|
||||
@@ -942,10 +945,12 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErr
|
||||
diagnostics: m.diagnostics))
|
||||
return nil
|
||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||
newInst.typ.flags.excl tfUnresolved
|
||||
newInst.typ.excl tfUnresolved
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
markUsed(c, info, s, isGenericInstance = false)
|
||||
onUse(info, s, isGenericInstance = false)
|
||||
markUsed(c, info, newInst, isGenericInstance = true)
|
||||
onUse(info, newInst, isGenericInstance = true)
|
||||
result = newSymNode(newInst, info)
|
||||
|
||||
proc setGenericParams(c: PContext, n, expectedParams: PNode) =
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
|
||||
## This module contains the data structures for the semantic checking phase.
|
||||
|
||||
import std/[tables, intsets, sets]
|
||||
import std/[tables, intsets, sets, strutils]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import
|
||||
options, ast, msgs, idents, renderer,
|
||||
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable
|
||||
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
|
||||
types, lowerings, trees, parampatterns, astalgo
|
||||
|
||||
import ic / ic
|
||||
|
||||
@@ -41,7 +42,7 @@ type
|
||||
breakInLoop*: bool # whether we are in a loop without block
|
||||
next*: PProcCon # used for stacking procedure contexts
|
||||
mappingExists*: bool
|
||||
mapping*: Table[ItemId, PSym]
|
||||
mapping*: SymMapping
|
||||
caseContext*: seq[tuple[n: PNode, idx: int]]
|
||||
localBindStmts*: seq[PNode]
|
||||
|
||||
@@ -152,7 +153,6 @@ type
|
||||
generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
|
||||
topStmts*: int # counts the number of encountered top level statements
|
||||
lastGenericIdx*: int # used for the generics stack
|
||||
hloLoopDetector*: int # used to prevent endless loops in the HLO
|
||||
inParallelStmt*: int
|
||||
instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
||||
op: TTypeAttachedOp; col: int): PSym {.nimcall.}
|
||||
@@ -171,8 +171,13 @@ type
|
||||
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
|
||||
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.
|
||||
forwardTypeUpdates*: seq[(PType, PNode)]
|
||||
# types that need to be updated in a type section
|
||||
# due to containing forward types, and their corresponding nodes
|
||||
inTypeofContext*: int
|
||||
|
||||
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
|
||||
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
@@ -254,7 +259,7 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
|
||||
|
||||
proc put*(p: PProcCon; key, val: PSym) =
|
||||
if not p.mappingExists:
|
||||
p.mapping = initTable[ItemId, PSym]()
|
||||
p.mapping = initSymMapping()
|
||||
p.mappingExists = true
|
||||
#echo "put into table ", key.info
|
||||
p.mapping[key.itemId] = val
|
||||
@@ -286,22 +291,24 @@ proc considerGenSyms*(c: PContext; n: PNode) =
|
||||
considerGenSyms(c, n[i])
|
||||
|
||||
proc newOptionEntry*(conf: ConfigRef): POptionEntry =
|
||||
new(result)
|
||||
result.options = conf.options
|
||||
result.defaultCC = ccNimCall
|
||||
result.dynlib = nil
|
||||
result.notes = conf.notes
|
||||
result.warningAsErrors = conf.warningAsErrors
|
||||
result = POptionEntry(
|
||||
options: conf.options,
|
||||
defaultCC: ccNimCall,
|
||||
dynlib: nil,
|
||||
notes: conf.notes,
|
||||
warningAsErrors: conf.warningAsErrors
|
||||
)
|
||||
|
||||
proc pushOptionEntry*(c: PContext): POptionEntry =
|
||||
new(result)
|
||||
var prev = c.optionStack[^1]
|
||||
result.options = c.config.options
|
||||
result.defaultCC = prev.defaultCC
|
||||
result.dynlib = prev.dynlib
|
||||
result.notes = c.config.notes
|
||||
result.warningAsErrors = c.config.warningAsErrors
|
||||
result.features = c.features
|
||||
let prev = c.optionStack[^1]
|
||||
result = POptionEntry(
|
||||
options: c.config.options,
|
||||
defaultCC: prev.defaultCC,
|
||||
dynlib: prev.dynlib,
|
||||
notes: c.config.notes,
|
||||
warningAsErrors: c.config.warningAsErrors,
|
||||
features: c.features
|
||||
)
|
||||
c.optionStack.add(result)
|
||||
|
||||
proc popOptionEntry*(c: PContext) =
|
||||
@@ -312,22 +319,23 @@ proc popOptionEntry*(c: PContext) =
|
||||
c.optionStack.setLen(c.optionStack.len - 1)
|
||||
|
||||
proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
||||
new(result)
|
||||
result.optionStack = @[newOptionEntry(graph.config)]
|
||||
result.libs = @[]
|
||||
result.module = module
|
||||
result.friendModules = @[module]
|
||||
result.converters = @[]
|
||||
result.patterns = @[]
|
||||
result.includedFiles = initIntSet()
|
||||
result.pureEnumFields = initStrTable()
|
||||
result.userPragmas = initStrTable()
|
||||
result.generics = @[]
|
||||
result.unknownIdents = initIntSet()
|
||||
result.cache = graph.cache
|
||||
result.graph = graph
|
||||
result.signatures = initStrTable()
|
||||
result.features = graph.config.features
|
||||
result = PContext(
|
||||
optionStack: @[newOptionEntry(graph.config)],
|
||||
libs: @[],
|
||||
module: module,
|
||||
friendModules: @[module],
|
||||
converters: @[],
|
||||
patterns: @[],
|
||||
includedFiles: initIntSet(),
|
||||
pureEnumFields: initStrTable(),
|
||||
userPragmas: initStrTable(),
|
||||
generics: @[],
|
||||
unknownIdents: initIntSet(),
|
||||
cache: graph.cache,
|
||||
graph: graph,
|
||||
signatures: initStrTable(),
|
||||
features: graph.config.features
|
||||
)
|
||||
if graph.config.symbolFiles != disabledSf:
|
||||
let id = module.position
|
||||
if graph.config.cmd != cmdM:
|
||||
@@ -391,8 +399,7 @@ proc reexportSym*(c: PContext; s: PSym) =
|
||||
addReexport(c.encoder, c.packedRepr, s)
|
||||
|
||||
proc newLib*(kind: TLibKind): PLib =
|
||||
new(result)
|
||||
result.kind = kind #result.syms = initObjectSet()
|
||||
result = PLib(kind: kind) #result.syms = initObjectSet()
|
||||
|
||||
proc addToLib*(lib: PLib, sym: PSym) =
|
||||
#if sym.annex != nil and not isGenericRoutine(sym):
|
||||
@@ -426,7 +433,7 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
|
||||
|
||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||
let typedesc = newTypeS(tyTypeDesc, c)
|
||||
incl typedesc.flags, tfCheckedForDestructor
|
||||
incl typedesc.flagsImpl, tfCheckedForDestructor
|
||||
internalAssert(c.config, typ != nil)
|
||||
typedesc.addSonSkipIntLit(typ, c.idgen)
|
||||
let sym = newSym(skType, c.cache.idAnon, c.idgen, getCurrOwner(c), info,
|
||||
@@ -459,8 +466,8 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||
result.rawAddSon t2
|
||||
propagateToOwner(result, t1)
|
||||
propagateToOwner(result, t2)
|
||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
result.flagsImpl.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flagsImpl.incl tfHasMeta
|
||||
|
||||
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||
if t1.kind != tyOr and t2.kind != tyOr:
|
||||
@@ -478,14 +485,14 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||
addOr(t2)
|
||||
propagateToOwner(result, t1)
|
||||
propagateToOwner(result, t2)
|
||||
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
result.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||
result.incl tfHasMeta
|
||||
|
||||
proc makeNotType*(c: PContext, t1: PType): PType =
|
||||
result = newTypeS(tyNot, c, son = t1)
|
||||
propagateToOwner(result, t1)
|
||||
result.flags.incl(t1.flags * {tfHasStatic})
|
||||
result.flags.incl tfHasMeta
|
||||
result.flagsImpl.incl(t1.flags * {tfHasStatic})
|
||||
result.flagsImpl.incl tfHasMeta
|
||||
|
||||
proc nMinusOne(c: PContext; n: PNode): PNode =
|
||||
result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n)
|
||||
@@ -495,7 +502,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
||||
let intType = getSysType(c.graph, n.info, tyInt)
|
||||
result = newTypeS(tyRange, c, son = intType)
|
||||
if n.typ != nil and n.typ.n == nil:
|
||||
result.flags.incl tfUnresolved
|
||||
result.incl tfUnresolved
|
||||
result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType),
|
||||
makeStaticExpr(c, nMinusOne(c, n)))
|
||||
|
||||
@@ -505,7 +512,7 @@ template rangeHasUnresolvedStatic*(t: PType): bool =
|
||||
proc errorType*(c: PContext): PType =
|
||||
## creates a type representing an error state
|
||||
result = newTypeS(tyError, c)
|
||||
result.flags.incl tfCheckedForDestructor
|
||||
result.flagsImpl.incl tfCheckedForDestructor
|
||||
|
||||
proc errorNode*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
@@ -555,12 +562,12 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
result = typ
|
||||
else:
|
||||
result = newTypeS(tyTypeDesc, c, skipIntLit(typ, c.idgen))
|
||||
incl result.flags, tfCheckedForDestructor
|
||||
incl result, tfCheckedForDestructor
|
||||
|
||||
proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
|
||||
if t.sym != nil: return t.sym
|
||||
result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info)
|
||||
result.flags.incl sfAnon
|
||||
result.flagsImpl.incl sfAnon
|
||||
result.typ = t
|
||||
|
||||
proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
|
||||
@@ -569,7 +576,7 @@ proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
|
||||
|
||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||
if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
|
||||
incl(s.flags, sfAddrTaken)
|
||||
incl(s.flagsImpl, sfAddrTaken)
|
||||
# XXX add to 'c' for global analysis
|
||||
|
||||
proc illFormedAst*(n: PNode; conf: ConfigRef) =
|
||||
@@ -635,3 +642,166 @@ proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
|
||||
## delegated to the "rod" file mechanism.
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
storeExpansion(c.encoder, c.packedRepr, info, expandedSym)
|
||||
|
||||
const
|
||||
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
|
||||
errXStackEscape = "address of '$1' may not escape its stack frame"
|
||||
|
||||
proc renderNotLValue*(n: PNode): string =
|
||||
result = $n
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if n.kind == nkHiddenCallConv and n.len > 1:
|
||||
result = $n[0] & "(" & result & ")"
|
||||
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
|
||||
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
|
||||
|
||||
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||
result = parampatterns.isAssignable(c.p.owner, n)
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
checkSonsLen(n, 1, c.config)
|
||||
result = n[0]
|
||||
else:
|
||||
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
|
||||
result.add n
|
||||
let aa = isAssignable(c, n)
|
||||
let sym = getRoot(n)
|
||||
if aa notin {arLValue, arLocalLValue}:
|
||||
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
|
||||
discard "allow access within a cast(unsafeAssign) section"
|
||||
elif strictDefs in c.features and aa == arAddressableConst and
|
||||
sym != nil and sym.kind == skLet and isOutParam:
|
||||
discard "allow let varaibles to be passed to out parameters"
|
||||
else:
|
||||
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
|
||||
|
||||
proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
result = n
|
||||
case n.kind
|
||||
of nkSym:
|
||||
# n.sym.typ can be nil in 'check' mode ...
|
||||
if n.sym.typ != nil and
|
||||
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
incl(n.sym.flagsImpl, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
of nkDotExpr:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
if n[1].kind != nkSym:
|
||||
internalError(c.config, n.info, "analyseIfAddressTaken")
|
||||
return
|
||||
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
incl(n[1].sym.flagsImpl, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
of nkBracketExpr:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
if n[0].kind == nkSym: incl(n[0].sym.flagsImpl, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
else:
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
|
||||
proc analyseIfAddressTakenInCall*(c: PContext, n: PNode, isConverter = false) =
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if n[0].typ == nil:
|
||||
# n[0] might be erroring node in nimsuggest
|
||||
return
|
||||
const
|
||||
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
||||
mSetLengthStr, mSetLengthSeq, mSetLengthSeqUninit, mAppendStrCh, mAppendStrStr, mSwap,
|
||||
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved}
|
||||
|
||||
template checkIfConverterCalled(c: PContext, n: PNode) =
|
||||
## Checks if there is a converter call which wouldn't be checked otherwise
|
||||
# Call can sometimes be wrapped in a deref
|
||||
let node = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if node.kind == nkHiddenCallConv:
|
||||
analyseIfAddressTakenInCall(c, node, true)
|
||||
# get the real type of the callee
|
||||
# it may be a proc var with a generic alias type, so we skip over them
|
||||
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
|
||||
# BUGFIX: check for L-Value still needs to be done for the arguments!
|
||||
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
|
||||
for i in 1..<n.len:
|
||||
if i < t.len and t[i] != nil and
|
||||
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
|
||||
let it = n[i]
|
||||
let aa = isAssignable(c, it)
|
||||
if aa notin {arLValue, arLocalLValue}:
|
||||
if it.kind != nkHiddenAddr:
|
||||
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
|
||||
discard "allow access within a cast(unsafeAssign) section"
|
||||
else:
|
||||
localError(c.config, it.info, errVarForOutParamNeededX % $it)
|
||||
# Make sure to still check arguments for converters
|
||||
c.checkIfConverterCalled(n[i])
|
||||
# bug #5113: disallow newSeq(result) where result is a 'var T':
|
||||
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
var arg = n[1] #.skipAddr
|
||||
if arg.kind == nkHiddenDeref: arg = arg[0]
|
||||
if arg.kind == nkSym and arg.sym.kind == skResult and
|
||||
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
|
||||
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
|
||||
|
||||
return
|
||||
for i in 1..<n.len:
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
c.checkIfConverterCalled(n[i])
|
||||
if i < t.len and
|
||||
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
|
||||
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
|
||||
# So we need to make sure we are checking them still when in a converter call
|
||||
if n[i].kind != nkHiddenAddr or isConverter:
|
||||
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
|
||||
|
||||
|
||||
proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
|
||||
## Replaces builtin generic hooks with lifted hooks.
|
||||
case kind
|
||||
of attachedDestructor:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedDestructor)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
|
||||
if n[1].kind == nkSym and n[1].sym.kind == skParam and
|
||||
n[1].typ.kind == tyVar:
|
||||
result[1] = genDeref(n[1])
|
||||
else:
|
||||
result[1] = skipAddr(n[1])
|
||||
of attachedTrace:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedTrace)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
of attachedDup:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedDup)
|
||||
if op != nil:
|
||||
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)
|
||||
result.add boolLit
|
||||
of attachedWasMoved:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedWasMoved)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
analyseIfAddressTakenInCall(c, result, false)
|
||||
of attachedSink:
|
||||
result = c.semAsgnOpr(c, n, nkSinkAsgn)
|
||||
of attachedAsgn:
|
||||
result = c.semAsgnOpr(c, n, nkAsgn)
|
||||
of attachedDeepCopy:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, kind)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
|
||||
@@ -16,7 +16,6 @@ when defined(nimCompilerStacktraceHints):
|
||||
const
|
||||
errExprXHasNoType = "expression '$1' has no type (or is ambiguous)"
|
||||
errXExpectsTypeOrValue = "'$1' expects a type or value"
|
||||
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
|
||||
errXStackEscape = "address of '$1' may not escape its stack frame"
|
||||
errExprHasNoAddress = "expression has no address"
|
||||
errCannotInterpretNodeX = "cannot evaluate '$1'"
|
||||
@@ -114,6 +113,8 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
|
||||
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
if result.kind == nkSym:
|
||||
markUsed(c, n.info, s)
|
||||
|
||||
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
|
||||
|
||||
@@ -411,7 +412,7 @@ proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType
|
||||
let baseType = semTypeNode(c, n[1], nil).skipTypes({tyTypeDesc})
|
||||
let t = newTypeS(targetType.kind, c, baseType)
|
||||
if targetType.kind == tyOwned:
|
||||
t.flags.incl tfHasOwned
|
||||
t.incl tfHasOwned
|
||||
result = newNodeI(nkType, n.info)
|
||||
result.typ() = makeTypeDesc(c, t)
|
||||
return
|
||||
@@ -725,7 +726,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
# nkBracket nodes can also be produced by the VM as seq constant nodes
|
||||
# in which case, we cannot produce a new array type for the node,
|
||||
# as this might lose type info even when the node has array type
|
||||
let constructType = n.typ.isNil
|
||||
let constructType = n.typ.isNil or n.typ.kind == tyFromExpr
|
||||
var expectedElementType, expectedIndexType: PType = nil
|
||||
var expectedBase: PType = nil
|
||||
if constructType:
|
||||
@@ -774,7 +775,11 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
|
||||
let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
|
||||
var typ: PType
|
||||
if constructType:
|
||||
var isGeneric = false
|
||||
if yy.typ != nil and yy.typ.kind == tyFromExpr:
|
||||
isGeneric = true
|
||||
typ = nil # will not be used
|
||||
elif constructType:
|
||||
typ = yy.typ
|
||||
if expectedElementType == nil:
|
||||
expectedElementType = typ
|
||||
@@ -799,11 +804,21 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
|
||||
let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
|
||||
result.add xx
|
||||
if constructType:
|
||||
if xx.typ != nil and xx.typ.kind == tyFromExpr:
|
||||
isGeneric = true
|
||||
elif constructType:
|
||||
typ = commonType(c, typ, xx.typ)
|
||||
#n[i] = semExprWithType(c, x, {})
|
||||
#result.add fitNode(c, typ, n[i])
|
||||
inc(lastIndex)
|
||||
if isGeneric:
|
||||
for i in 0..<result.len:
|
||||
if isIntLit(result[i].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
result[i].typ() = nil
|
||||
result.typ() = nil # current result.typ is invalid, index type is nil
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
if constructType:
|
||||
addSonSkipIntLit(result.typ, typ, c.idgen)
|
||||
for i in 0..<result.len:
|
||||
@@ -865,105 +880,6 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
|
||||
if hasUnresolvedArgs(c, n[i]): return true
|
||||
return false
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
checkSonsLen(n, 1, c.config)
|
||||
result = n[0]
|
||||
else:
|
||||
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
|
||||
result.add n
|
||||
let aa = isAssignable(c, n)
|
||||
let sym = getRoot(n)
|
||||
if aa notin {arLValue, arLocalLValue}:
|
||||
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
|
||||
discard "allow access within a cast(unsafeAssign) section"
|
||||
elif strictDefs in c.features and aa == arAddressableConst and
|
||||
sym != nil and sym.kind == skLet and isOutParam:
|
||||
discard "allow let varaibles to be passed to out parameters"
|
||||
else:
|
||||
localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
|
||||
|
||||
proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
|
||||
result = n
|
||||
case n.kind
|
||||
of nkSym:
|
||||
# n.sym.typ can be nil in 'check' mode ...
|
||||
if n.sym.typ != nil and
|
||||
skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
incl(n.sym.flags, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
of nkDotExpr:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
if n[1].kind != nkSym:
|
||||
internalError(c.config, n.info, "analyseIfAddressTaken")
|
||||
return
|
||||
if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
incl(n[1].sym.flags, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
of nkBracketExpr:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
|
||||
if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
else:
|
||||
result = newHiddenAddrTaken(c, n, isOutParam)
|
||||
|
||||
proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
if n[0].typ == nil:
|
||||
# n[0] might be erroring node in nimsuggest
|
||||
return
|
||||
const
|
||||
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
||||
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
|
||||
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove,
|
||||
mWasMoved}
|
||||
|
||||
template checkIfConverterCalled(c: PContext, n: PNode) =
|
||||
## Checks if there is a converter call which wouldn't be checked otherwise
|
||||
# Call can sometimes be wrapped in a deref
|
||||
let node = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
if node.kind == nkHiddenCallConv:
|
||||
analyseIfAddressTakenInCall(c, node, true)
|
||||
# get the real type of the callee
|
||||
# it may be a proc var with a generic alias type, so we skip over them
|
||||
var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
|
||||
# BUGFIX: check for L-Value still needs to be done for the arguments!
|
||||
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
|
||||
for i in 1..<n.len:
|
||||
if i < t.len and t[i] != nil and
|
||||
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
|
||||
let it = n[i]
|
||||
let aa = isAssignable(c, it)
|
||||
if aa notin {arLValue, arLocalLValue}:
|
||||
if it.kind != nkHiddenAddr:
|
||||
if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
|
||||
discard "allow access within a cast(unsafeAssign) section"
|
||||
else:
|
||||
localError(c.config, it.info, errVarForOutParamNeededX % $it)
|
||||
# Make sure to still check arguments for converters
|
||||
c.checkIfConverterCalled(n[i])
|
||||
# bug #5113: disallow newSeq(result) where result is a 'var T':
|
||||
if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
var arg = n[1] #.skipAddr
|
||||
if arg.kind == nkHiddenDeref: arg = arg[0]
|
||||
if arg.kind == nkSym and arg.sym.kind == skResult and
|
||||
arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}:
|
||||
localError(c.config, n.info, errXStackEscape % renderTree(n[1], {renderNoComments}))
|
||||
|
||||
return
|
||||
for i in 1..<n.len:
|
||||
let n = if n.kind == nkHiddenDeref: n[0] else: n
|
||||
c.checkIfConverterCalled(n[i])
|
||||
if i < t.len and
|
||||
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
|
||||
# Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
|
||||
# So we need to make sure we are checking them still when in a converter call
|
||||
if n[i].kind != nkHiddenAddr or isConverter:
|
||||
n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
|
||||
|
||||
include semmagic
|
||||
|
||||
proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
@@ -1003,7 +919,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
tfUnresolved notin n[i].typ.flags:
|
||||
break maybeLabelAsStatic
|
||||
n.typ() = newTypeS(tyStatic, c, n.typ)
|
||||
n.typ.flags.incl tfUnresolved
|
||||
n.typ.incl tfUnresolved
|
||||
|
||||
# optimization pass: not necessary for correctness of the semantic pass
|
||||
if (callee.kind == skConst or
|
||||
@@ -1035,11 +951,15 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||
result = evalStaticExpr(c.module, c.idgen, c.graph, call, c.p.owner)
|
||||
if result.isNil:
|
||||
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
|
||||
else: result = fixupTypeAfterEval(c, result, n)
|
||||
else:
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, n, producedClosure)
|
||||
else:
|
||||
result = evalConstExpr(c.module, c.idgen, c.graph, call)
|
||||
if result.isNil: result = n
|
||||
else: result = fixupTypeAfterEval(c, result, n)
|
||||
else:
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, n, producedClosure)
|
||||
else:
|
||||
result = n
|
||||
#if result != n:
|
||||
@@ -1057,7 +977,8 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
|
||||
result = c.graph.emptyNode
|
||||
else:
|
||||
result = fixupTypeAfterEval(c, result, a)
|
||||
var producedClosure = false
|
||||
result = fixupTypeAfterEval(c, result, a, producedClosure)
|
||||
|
||||
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||
flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
@@ -1376,7 +1297,9 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
# This seems semantically correct and then we'll be able
|
||||
# to return the section symbol directly here
|
||||
let foundType = makeTypeDesc(c, def[2].typ)
|
||||
return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info)
|
||||
let s = copySym(def[0].sym, c.idgen)
|
||||
s.typ = foundType
|
||||
return newSymNode(s, info)
|
||||
|
||||
of nkConstSection:
|
||||
for def in statement:
|
||||
@@ -1401,7 +1324,9 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
return c.graph.emptyNode
|
||||
else:
|
||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||
return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
|
||||
let s = copySym(tParam.sym, c.idgen)
|
||||
s.typ = foundTyp
|
||||
return newSymNode(s, info)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1873,7 +1798,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||
result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
|
||||
result.flags.incl nfDotSetter
|
||||
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
|
||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {efNoUndeclared})
|
||||
|
||||
if result != nil:
|
||||
result = afterCallActions(c, result, nOrig, {})
|
||||
@@ -1922,10 +1847,10 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
||||
if x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView):
|
||||
n[0] = x # 'result[]' --> 'result'
|
||||
n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent)
|
||||
x.typ.flags.incl tfVarIsPtr
|
||||
x.typ.incl tfVarIsPtr
|
||||
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
|
||||
elif sfGlobal in x.sym.flags:
|
||||
x.typ.flags.incl tfVarIsPtr
|
||||
x.typ.incl tfVarIsPtr
|
||||
|
||||
proc borrowCheck(c: PContext, n, le, ri: PNode) =
|
||||
const
|
||||
@@ -1995,7 +1920,7 @@ proc makeTupleAssignments(c: PContext; n: PNode): PNode =
|
||||
|
||||
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
|
||||
temp.typ = value.typ
|
||||
temp.flags.incl(sfGenSym)
|
||||
temp.flagsImpl.incl(sfGenSym)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||
@@ -2007,8 +1932,20 @@ proc makeTupleAssignments(c: PContext; n: PNode): PNode =
|
||||
|
||||
for i in 0..<lhs.len:
|
||||
if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
|
||||
# skip _ assignments if we are using a temp as they are already evaluated
|
||||
discard
|
||||
# tuple unpacking `skTemp` does not generate a destructor and
|
||||
# expects all fields to be unpacked, so instead of skipping,
|
||||
# generate `let _ = temp[i]` which should generate a destructor
|
||||
let utemp = newSym(skLet, lhs[i].ident, c.idgen, getCurrOwner(c), lhs[i].info)
|
||||
utemp.typ = value.typ[i]
|
||||
temp.flagsImpl.incl(sfGenSym)
|
||||
var uv = newNodeI(nkLetSection, lhs[i].info)
|
||||
let utempNode = newSymNode(utemp)
|
||||
var uvpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||
uvpart[0] = utempNode
|
||||
uvpart[1] = c.graph.emptyNode
|
||||
uvpart[2] = newTupleAccessRaw(tempNode, i)
|
||||
uv.add uvpart
|
||||
result.add(uv)
|
||||
else:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
|
||||
|
||||
@@ -2035,21 +1972,34 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
of nkBracketExpr:
|
||||
# a[i] = x
|
||||
# --> `[]=`(a, i, x)
|
||||
# try builtin subscript for LHS first:
|
||||
a = semSubscript(c, a, {efLValue})
|
||||
if a == nil:
|
||||
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
|
||||
result.add(n[1])
|
||||
if mode == noOverloadedSubscript:
|
||||
bracketNotFoundError(c, result, {})
|
||||
return errorNode(c, n)
|
||||
# `[]=` overloads failed and builtin subscript failed, try `[]` overloads for LHS
|
||||
# will error if not found:
|
||||
a = semExprWithType(c, n[0], {efLValue})
|
||||
else:
|
||||
# magic overload of `[]=` will always match so cannot check for mismatch here,
|
||||
# will go to above `if` branch instead
|
||||
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
|
||||
result.add(n[1])
|
||||
result = semExprNoType(c, result)
|
||||
return result
|
||||
of nkCurlyExpr:
|
||||
# a{i} = x --> `{}=`(a, i, x)
|
||||
# no builtin behavior/magic overloads for curly subscript,
|
||||
# try `{}=` overloads first then try `{}` overloads for LHS:
|
||||
let nOrig = n.copyTree
|
||||
result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "{}="))
|
||||
result.add(n[1])
|
||||
return semExprNoType(c, result)
|
||||
result = semOverloadedCallAnalyseEffects(c, result, result.copyTree, {efNoUndeclared})
|
||||
if result != nil:
|
||||
result = afterCallActions(c, result, nOrig, {})
|
||||
return
|
||||
else:
|
||||
# will error if `{}` overloads not found:
|
||||
a = semExprWithType(c, a, {efLValue})
|
||||
of nkPar, nkTupleConstr:
|
||||
if a.len >= 2 or a.kind == nkTupleConstr:
|
||||
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
|
||||
@@ -2174,7 +2124,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
||||
var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink})
|
||||
case t.kind
|
||||
of tyVar, tyLent:
|
||||
t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
|
||||
t.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
|
||||
if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}:
|
||||
n[0] = n[0][1]
|
||||
n[0] = takeImplicitAddr(c, n[0], t.kind == tyLent)
|
||||
@@ -2182,7 +2132,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
||||
for i in 0..<t.len:
|
||||
let e = skipTypes(t[i], {tyGenericInst, tyAlias, tySink})
|
||||
if e.kind in {tyVar, tyLent}:
|
||||
e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
|
||||
e.incl tfVarIsPtr # bugfix for #4048, #4910, #6892
|
||||
let tupleConstr = if n[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: n[0][1] else: n[0]
|
||||
if tupleConstr.kind in {nkPar, nkTupleConstr}:
|
||||
if tupleConstr[i].kind == nkExprColonExpr:
|
||||
@@ -2267,10 +2217,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
|
||||
result = someSym(c.graph, m, ident)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkOpenSymChoice, nkClosedSymChoice:
|
||||
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
|
||||
result = n[0].sym
|
||||
of nkOpenSym:
|
||||
result = lookUpForDeclared(c, n[0], onlyCurrentScope)
|
||||
else:
|
||||
localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
|
||||
result = nil
|
||||
@@ -2428,7 +2376,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||
processQuotations(c, quotedBlock, op, quotes, ids)
|
||||
|
||||
let dummyTemplateSym = newAnonSym(c, skTemplate, n.info)
|
||||
incl(dummyTemplateSym.flags, sfTemplateRedefinition)
|
||||
incl(dummyTemplateSym.flagsImpl, sfTemplateRedefinition)
|
||||
var dummyTemplate = newProcNode(
|
||||
nkTemplateDef, quotedBlock.info, body = quotedBlock,
|
||||
params = c.graph.emptyNode,
|
||||
@@ -2557,8 +2505,9 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
|
||||
# since it's an instantiation, we unmark it as a compilerproc. Otherwise
|
||||
# codegen would fail:
|
||||
if sfCompilerProc in result.flags:
|
||||
result.flags.excl {sfCompilerProc, sfExportc, sfImportc}
|
||||
result.loc.snippet = ""
|
||||
ensureMutable result
|
||||
result.flagsImpl.excl {sfCompilerProc, sfExportc, sfImportc}
|
||||
result.locImpl.snippet = ""
|
||||
|
||||
proc setMs(n: PNode, s: PSym): PNode =
|
||||
result = n
|
||||
@@ -2792,7 +2741,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||
result = newNodeI(nkCurly, n.info)
|
||||
result.typ() = newTypeS(tySet, c)
|
||||
result.typ.flags.incl tfIsConstructor
|
||||
result.typ.incl tfIsConstructor
|
||||
var expectedElementType: PType = nil
|
||||
if expectedType != nil and (
|
||||
let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
|
||||
@@ -2808,16 +2757,21 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||
else:
|
||||
# only semantic checking for all elements, later type checking:
|
||||
var typ: PType = nil
|
||||
var isGeneric = false
|
||||
for i in 0..<n.len:
|
||||
let doSetType = typ == nil
|
||||
if isRange(n[i]):
|
||||
checkSonsLen(n[i], 3, c.config)
|
||||
n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
|
||||
n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
|
||||
if doSetType:
|
||||
typ = skipTypes(n[i][1].typ,
|
||||
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
n[i].typ() = n[i][2].typ # range node needs type too
|
||||
if (n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr) or
|
||||
(n[i][2].typ != nil and n[i][2].typ.kind == tyFromExpr):
|
||||
isGeneric = true
|
||||
else:
|
||||
if doSetType:
|
||||
typ = skipTypes(n[i][1].typ,
|
||||
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
n[i].typ() = n[i][2].typ # range node needs type too
|
||||
elif n[i].kind == nkRange:
|
||||
# already semchecked
|
||||
if doSetType:
|
||||
@@ -2825,9 +2779,11 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
else:
|
||||
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
|
||||
if doSetType:
|
||||
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
|
||||
isGeneric = true
|
||||
elif doSetType:
|
||||
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
if doSetType:
|
||||
if doSetType and not isGeneric:
|
||||
if not isOrdinalType(typ, allowEnumWithHoles=true):
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
|
||||
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
||||
@@ -2842,6 +2798,14 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
||||
if expectedElementType == nil:
|
||||
expectedElementType = typ
|
||||
if isGeneric:
|
||||
for i in 0..<n.len:
|
||||
if isIntLit(n[i].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
n[i].typ() = nil
|
||||
result.add n[i]
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
addSonSkipIntLit(result.typ, typ, c.idgen)
|
||||
for i in 0..<n.len:
|
||||
var m: PNode
|
||||
@@ -2914,6 +2878,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
var typ = newTypeS(tyTuple, c)
|
||||
typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs
|
||||
var ids = initIntSet()
|
||||
var isGeneric = false
|
||||
for i in 0..<n.len:
|
||||
if n[i].kind != nkExprColonExpr:
|
||||
illFormedAst(n[i], c.config)
|
||||
@@ -2923,7 +2888,9 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
# can check if field name matches expected type here
|
||||
let expectedElemType = if expected != nil: expected[i] else: nil
|
||||
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
|
||||
if expectedElemType != nil and
|
||||
if n[i][1].typ != nil and n[i][1].typ.kind == tyFromExpr:
|
||||
isGeneric = true
|
||||
elif expectedElemType != nil and
|
||||
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
@@ -2944,7 +2911,21 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
typ.n.add newSymNode(f)
|
||||
n[i][0] = newSymNode(f)
|
||||
result.add n[i]
|
||||
if isGeneric:
|
||||
for i in 0..<result.len:
|
||||
if isIntLit(result[i][1].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
result[i][1].typ() = nil
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
let oldType = n.typ
|
||||
result.typ() = typ
|
||||
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
|
||||
# convert back to old type
|
||||
let conversion = indexTypesMatch(c, oldType, typ, result)
|
||||
# ignore matching error, the goal is just to keep the original type info
|
||||
if conversion != nil:
|
||||
result.typ() = oldType
|
||||
|
||||
proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
result = n # we don't modify n, but compute the type:
|
||||
@@ -2955,17 +2936,37 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
|
||||
if not (expected.kind == tyTuple and expected.len == n.len):
|
||||
expected = nil
|
||||
var typ = newTypeS(tyTuple, c) # leave typ.n nil!
|
||||
var isGeneric = false
|
||||
for i in 0..<n.len:
|
||||
let expectedElemType = if expected != nil: expected[i] else: nil
|
||||
n[i] = semExprWithType(c, n[i], {}, expectedElemType)
|
||||
if expectedElemType != nil and
|
||||
if n[i].typ != nil and n[i].typ.kind == tyFromExpr:
|
||||
isGeneric = true
|
||||
elif expectedElemType != nil and
|
||||
(expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
|
||||
# hasEmpty/nil check is to not break existing code like
|
||||
# `const foo = [(1, {}), (2, {false})]`,
|
||||
# `const foo = if true: (0, nil) else: (1, new(int))`
|
||||
n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
|
||||
let conversion = indexTypesMatch(c, expectedElemType, n[i].typ, n[i])
|
||||
# ignore matching error, full tuple will be matched later which may call converter, see #24609
|
||||
if conversion != nil:
|
||||
n[i] = conversion
|
||||
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
|
||||
if isGeneric:
|
||||
for i in 0..<result.len:
|
||||
if isIntLit(result[i].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
result[i].typ() = nil
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
return
|
||||
let oldType = n.typ
|
||||
result.typ() = typ
|
||||
if oldType != nil and not hasEmpty(oldType): # see hasEmpty comment above
|
||||
# convert back to old type
|
||||
let conversion = indexTypesMatch(c, oldType, typ, result)
|
||||
# ignore matching error, the goal is just to keep the original type info
|
||||
if conversion != nil:
|
||||
result.typ() = oldType
|
||||
|
||||
include semobjconstr
|
||||
|
||||
@@ -3054,9 +3055,15 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
s = nextOverloadIter(o, c, a)
|
||||
|
||||
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
|
||||
result = semTuplePositionsConstr(c, n, flags, expectedType)
|
||||
if result.typ.kind == tyFromExpr:
|
||||
# tyFromExpr is already ambivalent between types and values
|
||||
return
|
||||
var tupexp = result
|
||||
while tupexp.kind == nkHiddenSubConv: tupexp = tupexp[1]
|
||||
var isTupleType: bool = false
|
||||
if tupexp.len > 0: # don't interpret () as type
|
||||
internalAssert c.config, tupexp.kind == nkTupleConstr
|
||||
isTupleType = tupexp[0].typ.kind == tyTypeDesc
|
||||
# check if either everything or nothing is tyTypeDesc
|
||||
for i in 1..<tupexp.len:
|
||||
@@ -3066,15 +3073,13 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
result = n
|
||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
|
||||
result.typ() = makeTypeDesc(c, typ)
|
||||
else:
|
||||
result = tupexp
|
||||
|
||||
proc isExplicitGenericCall(c: PContext, n: PNode): bool =
|
||||
## checks if a call node `n` is a routine call with explicit generic params
|
||||
##
|
||||
##
|
||||
## the callee node needs to be either an nkBracketExpr or a call to a
|
||||
## symchoice of `[]` in which case it will be transformed into nkBracketExpr
|
||||
##
|
||||
##
|
||||
## the LHS of the bracket expr has to either be a symchoice or resolve to
|
||||
## a routine symbol
|
||||
template checkCallee(n: PNode) =
|
||||
@@ -3212,7 +3217,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNod
|
||||
a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
if a.kind == skEnumField:
|
||||
incl(a.flags, sfUsed)
|
||||
incl(a.flagsImpl, sfUsed)
|
||||
markOwnerModuleAsUsed(c, a)
|
||||
result.add newSymNode(a, info)
|
||||
onUse(info, a)
|
||||
@@ -3320,6 +3325,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
#performProcvarCheck(c, n, s)
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
if result.kind == nkSym:
|
||||
markUsed(c, n.info, s)
|
||||
markIndirect(c, result.sym)
|
||||
# if isGenericRoutine(result.sym):
|
||||
# localError(c.config, n.info, errInstantiateXExplicitly, s.name.s)
|
||||
@@ -3569,6 +3575,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
of nkMacroDef: result = semMacroDef(c, n)
|
||||
of nkTemplateDef: result = semTemplateDef(c, n)
|
||||
of nkImportStmt:
|
||||
trySuggestModuleNames(c, n)
|
||||
# this particular way allows 'import' in a 'compiles' context so that
|
||||
# template canImport(x): bool =
|
||||
# compiles:
|
||||
|
||||
@@ -36,7 +36,8 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
of nkIdent, nkSym:
|
||||
result = n
|
||||
let ident = considerQuotedIdent(c.c, n)
|
||||
if c.replaceByFieldName:
|
||||
if c.replaceByFieldName and
|
||||
ident.id != ord(wUnderscore):
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
|
||||
let fieldName = if c.tupleType.isNil: c.field.name.s
|
||||
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
|
||||
@@ -45,7 +46,8 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
return
|
||||
# other fields:
|
||||
for i in ord(c.replaceByFieldName)..<forLoop.len-2:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
|
||||
if ident.id == considerQuotedIdent(c.c, forLoop[i]).id and
|
||||
ident.id != ord(wUnderscore):
|
||||
var call = forLoop[^2]
|
||||
var tupl = call[i+1-ord(c.replaceByFieldName)]
|
||||
if c.field.isNil:
|
||||
|
||||
@@ -24,7 +24,7 @@ when defined(nimPreviewSlimSystem):
|
||||
proc errorType*(g: ModuleGraph): PType =
|
||||
## creates a type representing an error state
|
||||
result = newType(tyError, g.idgen, g.owners[^1])
|
||||
result.flags.incl tfCheckedForDestructor
|
||||
result.flagsImpl.incl tfCheckedForDestructor
|
||||
|
||||
proc getIntLitTypeG(g: ModuleGraph; literal: PNode; idgen: IdGenerator): PType =
|
||||
# we cache some common integer literal types for performance:
|
||||
@@ -471,19 +471,20 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo
|
||||
if result.kind == nkExprColonExpr: result = result[1]
|
||||
else:
|
||||
result = nil
|
||||
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
of nkBracket:
|
||||
idx -= toInt64(firstOrd(g.config, x.typ))
|
||||
if idx >= 0 and idx < x.len: result = x[int(idx)]
|
||||
else:
|
||||
result = nil
|
||||
localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
#localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
result = newNodeIT(nkCharLit, x.info, n.typ)
|
||||
if idx >= 0 and idx < x.strVal.len:
|
||||
result.intVal = ord(x.strVal[int(idx)])
|
||||
else:
|
||||
localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
|
||||
result = nil
|
||||
#localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
|
||||
else: result = nil
|
||||
|
||||
proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
|
||||
@@ -50,13 +50,13 @@ proc semGenericStmtScope(c: PContext, n: PNode,
|
||||
result = semGenericStmt(c, n, flags, ctx)
|
||||
closeScope(c)
|
||||
|
||||
template isMixedIn(sym): bool =
|
||||
template isMixedIn(sym): bool {.dirty.} =
|
||||
let s = sym
|
||||
s.name.id in ctx.toMixin or (withinConcept in flags and
|
||||
s.magic == mNone and
|
||||
s.kind in OverloadableSyms)
|
||||
|
||||
template canOpenSym(s): bool =
|
||||
template canOpenSym(s): bool {.dirty.} =
|
||||
{withinMixin, withinConcept} * flags == {withinMixin} and s.id notin ctx.toBind
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
@@ -65,7 +65,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
fromDotExpr=false): PNode =
|
||||
result = nil
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) =
|
||||
if fromDotExpr:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
@@ -274,7 +274,8 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
result = lookup(c, n, flags, ctx)
|
||||
if result != nil and result.kind == nkSym:
|
||||
assert result.sym != nil
|
||||
markUsed(c, n.info, result.sym)
|
||||
incl result.sym.flagsImpl, sfUsed
|
||||
markOwnerModuleAsUsed(c, result.sym)
|
||||
of nkDotExpr:
|
||||
#let luf = if withinMixin notin flags: {checkUndeclared} else: {}
|
||||
#var s = qualifiedLookUp(c, n, luf)
|
||||
@@ -317,7 +318,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
var first = int ord(withinConcept in flags)
|
||||
var mixinContext = false
|
||||
if s != nil:
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
mixinContext = s.magic in {mDefined, mDeclared, mDeclaredInScope, mCompiles, mAstToStr}
|
||||
let whichChoice = if s.id in ctx.toBind: scClosed
|
||||
elif s.isMixedIn: scForceOpen
|
||||
|
||||
@@ -24,7 +24,7 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) =
|
||||
let f = n.sym
|
||||
if f.kind == skField and fieldVisible(c, f):
|
||||
c.currentScope.symbols.strTableIncl(f, onConflictKeepOld=true)
|
||||
incl(f.flags, sfUsed)
|
||||
incl(f.flagsImpl, sfUsed)
|
||||
# it is not an error to shadow fields via parameters
|
||||
else: discard
|
||||
|
||||
@@ -42,7 +42,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: LayeredIdTable):
|
||||
if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses:
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
|
||||
s.flags.incl {sfUsed, sfFromGeneric}
|
||||
s.flagsImpl.incl {sfUsed, sfFromGeneric}
|
||||
var t = lookup(pt, q.typ)
|
||||
if t == nil:
|
||||
if tfRetType in q.typ.flags:
|
||||
@@ -149,7 +149,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
||||
nil
|
||||
b = semProcBody(c, b, resultType)
|
||||
result.ast[bodyPos] = hloBody(c, b)
|
||||
excl(result.flags, sfForward)
|
||||
excl(result, sfForward)
|
||||
trackProc(c, result, result.ast[bodyPos])
|
||||
dec c.inGenericInst
|
||||
|
||||
@@ -208,7 +208,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||
|
||||
# this scope was not created by the user,
|
||||
# unused params shouldn't be reported.
|
||||
param.flags.incl sfUsed
|
||||
param.flagsImpl.incl sfUsed
|
||||
addDecl(c, param)
|
||||
|
||||
result = replaceTypeVarsT(cl, header)
|
||||
@@ -257,7 +257,7 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
let needsStaticSkipping = resulti.kind == tyFromExpr
|
||||
let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
|
||||
if resulti.kind == tyFromExpr:
|
||||
resulti.flags.incl tfNonConstExpr
|
||||
resulti.incl tfNonConstExpr
|
||||
result[i] = replaceTypeVarsT(cl, resulti)
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
@@ -282,7 +282,7 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.typ.kind == tyFromExpr:
|
||||
def.typ.flags.incl tfNonConstExpr
|
||||
def.typ.incl tfNonConstExpr
|
||||
if not isIntLit(def.typ):
|
||||
def = prepareNode(cl, def)
|
||||
|
||||
@@ -308,6 +308,8 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
param.typ = result[i]
|
||||
|
||||
result.n[i] = newSymNode(param)
|
||||
if isRecursiveStructuralType(result[i]):
|
||||
localError(c.config, originalParams[i].sym.info, "illegal recursion in type '" & typeToString(result[i]) & "'")
|
||||
propagateToOwner(result, result[i])
|
||||
addDecl(c, param)
|
||||
|
||||
@@ -318,6 +320,8 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
cl.isReturnType = false
|
||||
result.n[0] = originalParams[0].copyTree
|
||||
if result[0] != nil:
|
||||
if isRecursiveStructuralType(result[0]):
|
||||
localError(c.config, originalParams[0].info, "illegal recursion in type '" & typeToString(result[0]) & "'")
|
||||
propagateToOwner(result, result[0])
|
||||
|
||||
eraseVoidParams(result)
|
||||
@@ -333,7 +337,7 @@ proc instantiateOnlyProcType(c: PContext, pt: LayeredIdTable, prc: PSym, info: T
|
||||
# examples are in texplicitgenerics
|
||||
# might be buggy, see rest of generateInstance if problems occur
|
||||
let fakeSym = copySym(prc, c.idgen)
|
||||
incl(fakeSym.flags, sfFromGeneric)
|
||||
incl(fakeSym.flagsImpl, sfFromGeneric)
|
||||
fakeSym.instantiatedFrom = prc
|
||||
openScope(c)
|
||||
for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
|
||||
@@ -389,7 +393,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
let oldScope = c.currentScope
|
||||
while not isTopLevel(c): c.currentScope = c.currentScope.parent
|
||||
result = copySym(fn, c.idgen)
|
||||
incl(result.flags, sfFromGeneric)
|
||||
incl(result, sfFromGeneric)
|
||||
result.instantiatedFrom = fn
|
||||
if sfGlobal in result.flags and c.config.symbolFiles != disabledSf:
|
||||
let passc = getLocalPassC(c, producer)
|
||||
@@ -425,6 +429,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
addDecl(c, s)
|
||||
entry.concreteTypes[i] = s.typ
|
||||
inc i
|
||||
entry.genericParamsCount = i
|
||||
c.matchedConcept = nil
|
||||
pushProcCon(c, result)
|
||||
instantiateProcType(c, pt, result, info)
|
||||
@@ -433,7 +438,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: LayeredIdTable,
|
||||
inc i
|
||||
#echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
|
||||
if tfTriggersCompileTime in result.typ.flags:
|
||||
incl(result.flags, sfCompileTime)
|
||||
incl(result, sfCompileTime)
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
|
||||
if oldPrc == nil:
|
||||
|
||||
@@ -86,7 +86,7 @@ proc ithField(t: PType, field: var FieldTracker): FieldInfo =
|
||||
base = b.baseClass
|
||||
result = ithField(t.n, field)
|
||||
|
||||
proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var bool) =
|
||||
let x = t.skipTypes(abstractInst+{tyRange})
|
||||
# Note: x can be unequal to t and we need to be careful to use 't'
|
||||
# to not to skip tyGenericInst
|
||||
@@ -102,7 +102,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
globalError conf, n.info, "invalid field at index " & $i
|
||||
else:
|
||||
internalAssert(conf, n[i].kind == nkExprColonExpr)
|
||||
annotateType(n[i][1], field.sym.typ, conf)
|
||||
annotateType(n[i][1], field.sym.typ, conf, producedClosure)
|
||||
if field.delete:
|
||||
# only codegen fields from active case branches
|
||||
incl(n[i].flags, nfPreventCg)
|
||||
@@ -111,9 +111,11 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
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)
|
||||
else: annotateType(n[i], x[i], conf, producedClosure)
|
||||
elif x.kind == tyProc and x.callConv == ccClosure:
|
||||
n.typ() = t
|
||||
if n.len > 1 and n[1].kind notin {nkEmpty, nkNilLit}:
|
||||
producedClosure = true
|
||||
elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
|
||||
if n.kind == nkTupleConstr:
|
||||
let
|
||||
@@ -125,11 +127,11 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
of nkStrKinds:
|
||||
for i in left..right:
|
||||
bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i])
|
||||
annotateType(bracketExpr[^1], x.elementType, conf)
|
||||
annotateType(bracketExpr[^1], x.elementType, conf, producedClosure)
|
||||
of nkBracket:
|
||||
for i in left..right:
|
||||
bracketExpr.add n[0][i]
|
||||
annotateType(bracketExpr[^1], x.elementType, conf)
|
||||
annotateType(bracketExpr[^1], x.elementType, conf, producedClosure)
|
||||
else:
|
||||
globalError(conf, n.info, "Incorrectly generated tuple constr")
|
||||
n[] = bracketExpr[]
|
||||
@@ -140,13 +142,18 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
|
||||
of nkBracket:
|
||||
if x.kind in {tyArray, tySequence, tyOpenArray}:
|
||||
n.typ() = t
|
||||
for m in n: annotateType(m, x.elemType, conf)
|
||||
for m in n: annotateType(m, x.elemType, conf, producedClosure)
|
||||
else:
|
||||
globalError(conf, n.info, "[] must have some form of array type")
|
||||
of nkCurly:
|
||||
if x.kind in {tySet}:
|
||||
n.typ() = t
|
||||
for m in n: annotateType(m, x.elemType, conf)
|
||||
for m in n:
|
||||
if m.kind == nkRange:
|
||||
annotateType(m[0], x.elemType, conf, producedClosure)
|
||||
annotateType(m[1], x.elemType, conf, producedClosure)
|
||||
else:
|
||||
annotateType(m, x.elemType, conf, producedClosure)
|
||||
else:
|
||||
globalError(conf, n.info, "{} must have the set type")
|
||||
of nkFloatLit..nkFloat128Lit:
|
||||
|
||||
@@ -34,11 +34,11 @@ proc semAddr(c: PContext; n: PNode): PNode =
|
||||
result = newNodeI(nkAddr, n.info)
|
||||
let x = semExprWithType(c, n)
|
||||
if x.kind == nkSym:
|
||||
x.sym.flags.incl(sfAddrTaken)
|
||||
x.sym.flagsImpl.incl(sfAddrTaken)
|
||||
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.skipTypes({tySink}))
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
@@ -54,8 +54,16 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
result.add typExpr
|
||||
if typExpr.typ.kind == tyFromExpr:
|
||||
typExpr.typ.flags.incl tfNonConstExpr
|
||||
result.typ() = makeTypeDesc(c, typExpr.typ)
|
||||
typExpr.typ.incl tfNonConstExpr
|
||||
var t = typExpr.typ
|
||||
if t.kind == tyStatic:
|
||||
let base = t.skipTypes({tyStatic})
|
||||
if c.inGenericContext > 0 and base.containsGenericType:
|
||||
t = makeTypeFromExpr(c, copyTree(typExpr))
|
||||
t.incl tfNonConstExpr
|
||||
else:
|
||||
t = base
|
||||
result.typ() = makeTypeDesc(c, t)
|
||||
|
||||
type
|
||||
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
|
||||
@@ -77,7 +85,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
# expression is compiled early in a generic body
|
||||
result = semGenericStmt(c, x)
|
||||
result.typ() = makeTypeFromExpr(c, copyTree(result))
|
||||
result.typ.flags.incl tfNonConstExpr
|
||||
result.typ.incl tfNonConstExpr
|
||||
return
|
||||
let s = # extract sym from first arg
|
||||
if n.len > 1:
|
||||
@@ -434,7 +442,7 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
copyTypeProps(c.graph, c.idgen.module, result, t)
|
||||
|
||||
result[^1] = b
|
||||
result.flags.excl tfHasOwned
|
||||
result.excl tfHasOwned
|
||||
else:
|
||||
result = t
|
||||
else:
|
||||
@@ -463,7 +471,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
|
||||
result = copySym(orig, c.idgen)
|
||||
result.info = info
|
||||
result.flags.incl sfFromGeneric
|
||||
result.incl sfFromGeneric
|
||||
setOwner(result, orig)
|
||||
let origParamType = orig.typ.firstParamType
|
||||
let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
|
||||
@@ -543,7 +551,7 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
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
|
||||
wrapperSym.flagsImpl.incl sfUsed
|
||||
|
||||
let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
|
||||
params = nkFormalParams.newTree(c.graph.emptyNode,
|
||||
@@ -604,9 +612,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mArrPut:
|
||||
result = semArrPut(c, n, flags)
|
||||
of mAsgn:
|
||||
if n[0].sym.name.s == "=":
|
||||
case n[0].sym.name.s
|
||||
of "=", "=copy":
|
||||
result = semAsgnOpr(c, n, nkAsgn)
|
||||
elif n[0].sym.name.s == "=sink":
|
||||
of "=sink":
|
||||
result = semAsgnOpr(c, n, nkSinkAsgn)
|
||||
else:
|
||||
result = semShallowCopy(c, n, flags)
|
||||
@@ -645,49 +654,20 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mNewFinalize:
|
||||
result = semNewFinalize(c, n)
|
||||
of mDestroy:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedDestructor)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
|
||||
if n[1].kind == nkSym and n[1].sym.kind == skParam and
|
||||
n[1].typ.kind == tyVar:
|
||||
result[1] = genDeref(n[1])
|
||||
else:
|
||||
result[1] = skipAddr(n[1])
|
||||
result = replaceHookMagic(c, n, attachedDestructor)
|
||||
of mTrace:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedTrace)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
result = replaceHookMagic(c, n, attachedTrace)
|
||||
of mDup:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedDup)
|
||||
if op != nil:
|
||||
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)
|
||||
result.add boolLit
|
||||
result = replaceHookMagic(c, n, attachedDup)
|
||||
of mWasMoved:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedWasMoved)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
let addrExp = newNodeIT(nkHiddenAddr, result[1].info, makePtrType(c, t))
|
||||
addrExp.add result[1]
|
||||
result[1] = addrExp
|
||||
result = replaceHookMagic(c, n, attachedWasMoved)
|
||||
of mUnown:
|
||||
result = semUnown(c, n)
|
||||
of mExists, mForall:
|
||||
result = semQuantifier(c, n)
|
||||
of mOld:
|
||||
result = semOld(c, n)
|
||||
of mSetLengthSeq:
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
result = n
|
||||
let seqType = result[1].typ.skipTypes({tyPtr, tyRef, # in case we had auto-dereferencing
|
||||
tyVar, tyGenericInst, tyOwned, tySink,
|
||||
|
||||
@@ -68,6 +68,10 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
|
||||
let assignment = initExpr[i]
|
||||
if assignment.kind != nkExprColonExpr:
|
||||
invalidObjConstr(c, assignment)
|
||||
elif nfPreventCg in assignment.flags:
|
||||
# this is an object constructor node generated by the VM and
|
||||
# this field is in an inactive case branch, just ignore it
|
||||
discard
|
||||
elif fieldId == considerQuotedIdent(c, assignment[0]).id:
|
||||
return assignment
|
||||
|
||||
@@ -236,7 +240,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
let prevFields = fieldsPresentInBranch(selectedBranch)
|
||||
let currentFields = fieldsPresentInBranch(i)
|
||||
localError(c.config, constrCtx.initExpr.info,
|
||||
("The fields '$1' and '$2' cannot be initialized together, " &
|
||||
("The fields $1 and $2 cannot be initialized together, " &
|
||||
"because they are from conflicting branches in the case object.") %
|
||||
[prevFields, currentFields])
|
||||
result.status = initConflict
|
||||
@@ -481,7 +485,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
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
|
||||
result.typ.incl tfHasOwned
|
||||
if t.kind != tyObject:
|
||||
return localErrorNode(c, result, if t.kind != tyGenericBody:
|
||||
"object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))
|
||||
@@ -515,6 +519,10 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
invalidObjConstr(c, field)
|
||||
hasError = true
|
||||
continue
|
||||
elif nfPreventCg in field.flags:
|
||||
# this is an object constructor node generated by the VM and
|
||||
# this field is in an inactive case branch, just ignore it
|
||||
continue
|
||||
let id = considerQuotedIdent(c, field[0])
|
||||
# This node was not processed. There are two possible reasons:
|
||||
# 1) It was shadowed by a field with the same name on the left
|
||||
|
||||
@@ -491,7 +491,7 @@ proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): P
|
||||
var varSection = newNodeI(nkVarSection, n.info)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "barrier"), idgen, owner, n.info)
|
||||
temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flagsImpl, sfFromGeneric)
|
||||
let tempNode = newSymNode(temp)
|
||||
varSection.addVar tempNode
|
||||
|
||||
|
||||
@@ -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, pushpoppragmas
|
||||
semstrictfuncs, suggestsymdb, pushpoppragmas, lowerings
|
||||
|
||||
import std/[tables, intsets, strutils, sequtils]
|
||||
|
||||
@@ -84,6 +84,7 @@ type
|
||||
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
isInnerProc: bool
|
||||
inEnforcedNoSideEffects: bool
|
||||
unknownRaises: seq[(PSym, TLineInfo)]
|
||||
currOptions: TOptions
|
||||
optionsStack: seq[(TOptions, TNoteKinds)]
|
||||
config: ConfigRef
|
||||
@@ -137,9 +138,10 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit
|
||||
createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info)
|
||||
|
||||
createTypeBoundOps(tracked.graph, tracked.c, typ, info, tracked.c.idgen)
|
||||
if (tfHasAsgn in typ.flags) or
|
||||
optSeqDestructors in tracked.config.globalOptions:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
if tracked.config.selectedGC == gcRefc or
|
||||
optSeqDestructors in tracked.config.globalOptions or
|
||||
tfHasAsgn in typ.flags:
|
||||
tracked.owner.incl sfInjectDestructors
|
||||
|
||||
proc isLocalSym(a: PEffects, s: PSym): bool =
|
||||
s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
|
||||
@@ -204,7 +206,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
|
||||
|
||||
proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
|
||||
if a.inTryStmt > 0 and a.config.exc == excSetjmp:
|
||||
incl(s.flags, sfVolatile)
|
||||
incl(s, sfVolatile)
|
||||
|
||||
proc varDecl(a: PEffects; n: PNode) {.inline.} =
|
||||
if n.kind == nkSym:
|
||||
@@ -370,14 +372,16 @@ proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) =
|
||||
|
||||
proc useVar(a: PEffects, n: PNode) =
|
||||
let s = n.sym
|
||||
if a.inExceptOrFinallyStmt > 0:
|
||||
incl s.flags, sfUsedInFinallyOrExcept
|
||||
if isLocalSym(a, s):
|
||||
if a.inExceptOrFinallyStmt > 0:
|
||||
incl s, sfUsedInFinallyOrExcept
|
||||
if sfNoInit in s.flags:
|
||||
# If the variable is explicitly marked as .noinit. do not emit any error
|
||||
a.init.add s.id
|
||||
elif s.id notin a.init:
|
||||
if s.typ.requiresInit:
|
||||
if s.kind == skResult and tfRequiresInit in s.typ.flags:
|
||||
localError(a.config, n.info, "'result' requires explicit initialization")
|
||||
elif s.typ.requiresInit:
|
||||
message(a.config, n.info, warnProveInit, s.name.s)
|
||||
elif a.leftPartOfAsgn <= 0:
|
||||
if strictDefs in a.c.features:
|
||||
@@ -638,6 +642,8 @@ proc importedFromC(n: PNode): bool =
|
||||
proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||
let pragma = s.ast[pragmasPos]
|
||||
let spec = effectSpec(pragma, wRaises)
|
||||
if spec.isNil and sfForward in s.flags:
|
||||
tracked.unknownRaises.add (s, n.info)
|
||||
mergeRaises(tracked, spec, n)
|
||||
|
||||
let tagSpec = effectSpec(pragma, wTags)
|
||||
@@ -701,7 +707,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
|
||||
assert n.kind == nkEffectList
|
||||
n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil and n[forbiddenEffects] == nil)
|
||||
|
||||
proc isTrival(caller: PNode): bool {.inline.} =
|
||||
proc isTrivial(caller: PNode): bool {.inline.} =
|
||||
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap}
|
||||
|
||||
proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
|
||||
@@ -710,7 +716,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
|
||||
let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil
|
||||
# assume indirect calls are taken here:
|
||||
if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
|
||||
not isTrival(caller) and
|
||||
not isTrivial(caller) and
|
||||
((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures):
|
||||
|
||||
internalAssert tracked.config, op.n[0].kind == nkEffectList
|
||||
@@ -882,8 +888,9 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
setLen(tracked.guards.s, oldFacts)
|
||||
dec tracked.inIfStmt
|
||||
|
||||
proc trackBlock(tracked: PEffects, n: PNode) =
|
||||
proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
let myBlock = tracked.currentBlock
|
||||
var oldState = -1
|
||||
for i in 0..<n.len:
|
||||
if hasSubnodeWith(n[i], nkBreakStmt):
|
||||
@@ -895,6 +902,14 @@ proc trackBlock(tracked: PEffects, n: PNode) =
|
||||
if oldState < 0: oldState = tracked.init.len
|
||||
track(tracked, n[i])
|
||||
if oldState > 0: setLen(tracked.init, oldState)
|
||||
if typ != nil and typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs}:
|
||||
let last = lastSon(n)
|
||||
let root = getRoot(last)
|
||||
if root != nil:
|
||||
let owner = tracked.scopes.getOrDefault(root.id, -1)
|
||||
if owner >= 0:
|
||||
localError(tracked.config, last.info, "'" & renderTree(last) & "' borrows from location '" & root.name.s &
|
||||
"' which does not live long enough")
|
||||
else:
|
||||
track(tracked, n)
|
||||
|
||||
@@ -967,7 +982,7 @@ proc checkForSink(tracked: PEffects; n: PNode) =
|
||||
proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
when defined(nimsuggest):
|
||||
proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) =
|
||||
var si = q.findSymInfoIndex(info)
|
||||
var si = q.findSymInfoIndex(info, true)
|
||||
if si != -1:
|
||||
q.caughtExceptionsSet[si] = true
|
||||
for w1 in tracked.caughtExceptions.nodes:
|
||||
@@ -977,6 +992,25 @@ proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s:
|
||||
if optIdeExceptionInlayHints in tracked.config.globalOptions:
|
||||
internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)
|
||||
|
||||
proc findHookKind(name: string): (bool, TTypeAttachedOp) =
|
||||
case name.normalize
|
||||
of "=wasmoved":
|
||||
result = (true, attachedWasMoved)
|
||||
of "=destroy":
|
||||
result = (true, attachedDestructor)
|
||||
of "=copy", "=":
|
||||
result = (true, attachedAsgn)
|
||||
of "=dup":
|
||||
result = (true, attachedDup)
|
||||
of "=sink":
|
||||
result = (true, attachedSink)
|
||||
of "=trace":
|
||||
result = (true, attachedTrace)
|
||||
of "=deepcopy":
|
||||
result = (true, attachedDeepCopy)
|
||||
else:
|
||||
result = (false, attachedWasMoved)
|
||||
|
||||
proc trackCall(tracked: PEffects; n: PNode) =
|
||||
template gcsafeAndSideeffectCheck() =
|
||||
if notGcSafe(op) and not importedFromC(a):
|
||||
@@ -1065,18 +1099,17 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
checkBounds(tracked, n[1], n[2])
|
||||
|
||||
|
||||
var n = n
|
||||
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
|
||||
tracked.owner.kind != skMacro:
|
||||
var opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
|
||||
if a.sym.name.s == "=": opKind = attachedAsgn.int
|
||||
if opKind != -1:
|
||||
var (isHook, opKind) = findHookKind(a.sym.name.s)
|
||||
if isHook:
|
||||
# rebind type bounds operations after createTypeBoundOps call
|
||||
let t = n[1].typ.skipTypes({tyAlias, tyVar})
|
||||
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
|
||||
if a.sym != getAttachedOp(tracked.graph, t, opKind):
|
||||
createTypeBoundOps(tracked, t, n.info, explicit = true)
|
||||
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
|
||||
if op != nil:
|
||||
n[0].sym = op
|
||||
# replace builtin hooks with lifted ones
|
||||
n = replaceHookMagic(tracked.c, n, opKind)
|
||||
|
||||
if op != nil and op.kind == tyProc:
|
||||
for i in 1..<min(n.safeLen, op.signatureLen):
|
||||
@@ -1210,7 +1243,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of nkSym:
|
||||
useVar(tracked, n)
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
tracked.owner.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ() = n.sym.typ
|
||||
of nkHiddenAddr, nkAddr:
|
||||
@@ -1309,12 +1342,12 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
tracked.init.setLen(oldState)
|
||||
track(tracked, n[1][0])
|
||||
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1], n.typ)
|
||||
of nkWhileStmt:
|
||||
# 'while true' loop?
|
||||
inc tracked.currentBlock
|
||||
if isTrue(n[0]):
|
||||
trackBlock(tracked, n[1])
|
||||
trackBlock(tracked, n[1], nil)
|
||||
else:
|
||||
# loop may never execute:
|
||||
let oldState = tracked.init.len
|
||||
@@ -1505,7 +1538,7 @@ proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
|
||||
|
||||
proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg: string, hints: bool;
|
||||
effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.};
|
||||
hintsArg: PNode = nil; isForbids: bool = false) =
|
||||
hintsArg: PNode = nil; isForbids: bool = false; unknownRaises: seq[(PSym, TLineInfo)] = @[]) =
|
||||
# check that any real exception is listed in 'spec'; mark those as used;
|
||||
# report any unused exception
|
||||
var used = initIntSet()
|
||||
@@ -1522,6 +1555,8 @@ proc checkRaisesSpec(g: ModuleGraph; emitWarnings: bool; spec, real: PNode, msg:
|
||||
pushInfoContext(g.config, spec.info)
|
||||
var rr = if r.kind == nkRaiseStmt: r[0] else: r
|
||||
while rr.kind in {nkStmtList, nkStmtListExpr} and rr.len > 0: rr = rr.lastSon
|
||||
for (s, info) in unknownRaises.items:
|
||||
message(g.config, info, hintUnknownRaises, s.name.s)
|
||||
message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated,
|
||||
renderTree(rr) & " " & msg & typeToString(r.typ))
|
||||
popInfoContext(g.config)
|
||||
@@ -1592,7 +1627,7 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) =
|
||||
effects[pragmasEffects] = n
|
||||
if s != nil and s.magic != mNone:
|
||||
if s.magic != mEcho:
|
||||
t.flags.incl tfNoSideEffect
|
||||
t.incl tfNoSideEffect
|
||||
|
||||
proc rawInitEffects(g: ModuleGraph; effects: PNode) =
|
||||
newSeq(effects.sons, effectListLen)
|
||||
@@ -1647,7 +1682,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
t.scopes[res.id] = t.currentBlock
|
||||
if sfNoInit in s.flags:
|
||||
# marks result "noinit"
|
||||
incl res.flags, sfNoInit
|
||||
incl res, sfNoInit
|
||||
|
||||
track(t, body)
|
||||
|
||||
@@ -1679,7 +1714,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
if not isNil(raisesSpec):
|
||||
let useWarning = s.name.s == "=destroy"
|
||||
checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
|
||||
hints=on, subtypeRelation, hintsArg=s.ast[0])
|
||||
hints=on, subtypeRelation, hintsArg=s.ast[0], unknownRaises = t.unknownRaises)
|
||||
# after the check, use the formal spec:
|
||||
effects[exceptionEffects] = raisesSpec
|
||||
else:
|
||||
@@ -1734,9 +1769,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
else:
|
||||
localError(g.config, s.info, "") # simple error for `system.compiles` context
|
||||
if not t.gcUnsafe:
|
||||
s.typ.flags.incl tfGcSafe
|
||||
s.typ.incl tfGcSafe
|
||||
if not t.hasSideEffect and sfSideEffect notin s.flags:
|
||||
s.typ.flags.incl tfNoSideEffect
|
||||
s.typ.incl tfNoSideEffect
|
||||
when defined(drnim):
|
||||
if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, s, body)
|
||||
when defined(useDfa):
|
||||
|
||||
@@ -79,7 +79,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
|
||||
if s.kind == skLabel and s.owner.id == c.p.owner.id:
|
||||
var x = newSymNode(s)
|
||||
x.info = n.info
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
n[0] = x
|
||||
suggestSym(c.graph, x.info, s, c.graph.usageSym)
|
||||
onUse(x.info, s)
|
||||
@@ -394,8 +394,9 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
elif a.len == 1:
|
||||
# count number of ``except: body`` blocks
|
||||
inc catchAllExcepts
|
||||
message(c.config, a.info, warnBareExcept,
|
||||
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||
if noPanicOnExcept in c.graph.config.legacyFeatures:
|
||||
message(c.config, a.info, warnBareExcept,
|
||||
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||
else:
|
||||
# support ``except KeyError, ValueError, ... : body``
|
||||
if catchAllExcepts > 0:
|
||||
@@ -483,13 +484,13 @@ proc identWithin(n: PNode, s: PIdent): bool =
|
||||
proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym =
|
||||
if isTopLevel(c):
|
||||
result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true)
|
||||
incl(result.flags, sfGlobal)
|
||||
incl(result, sfGlobal)
|
||||
#if kind in {skVar, skLet}:
|
||||
# echo "global variable here ", n.info, " ", result.name.s
|
||||
else:
|
||||
result = semIdentWithPragma(c, kind, n, {})
|
||||
if result.owner.kind == skModule:
|
||||
incl(result.flags, sfGlobal)
|
||||
incl(result, sfGlobal)
|
||||
result.options = c.config.options
|
||||
|
||||
if reportToNimsuggest:
|
||||
@@ -520,7 +521,7 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||
|
||||
proc isDiscardUnderscore(v: PSym): bool =
|
||||
if v.name.id == ord(wUnderscore):
|
||||
v.flags.incl(sfGenSym)
|
||||
v.incl(sfGenSym)
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
@@ -725,22 +726,33 @@ template isLocalSym(sym: PSym): bool =
|
||||
sym.typ.kind == tyTypeDesc or
|
||||
sfCompileTime in sym.flags) or
|
||||
sym.kind in {skProc, skFunc, skIterator} and
|
||||
sfGlobal notin sym.flags
|
||||
|
||||
template isLocalVarSym(n: PNode): bool =
|
||||
n.kind == nkSym and isLocalSym(n.sym)
|
||||
sfGlobal notin sym.flags and sym.typ.callConv == ccClosure
|
||||
|
||||
proc usesLocalVar(n: PNode): bool =
|
||||
result = false
|
||||
for z in 1 ..< n.len:
|
||||
if n[z].isLocalVarSym:
|
||||
return true
|
||||
elif n[z].kind in nkCallKinds:
|
||||
if usesLocalVar(n[z]):
|
||||
case n.kind
|
||||
of nkSym:
|
||||
result = isLocalSym(n.sym)
|
||||
of nkCallKinds, nkObjConstr:
|
||||
result = false
|
||||
for i in 1 ..< n.len:
|
||||
if usesLocalVar(n[i]):
|
||||
return true
|
||||
of nkTupleConstr, nkPar, nkBracket, nkCurly:
|
||||
result = false
|
||||
for i in 0 ..< n.len:
|
||||
if usesLocalVar(n[i]):
|
||||
return true
|
||||
of nkDotExpr, nkCheckedFieldExpr,
|
||||
nkBracketExpr, nkAddr, nkHiddenAddr,
|
||||
nkObjDownConv, nkObjUpConv:
|
||||
result = usesLocalVar(n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkExprColonExpr:
|
||||
result = usesLocalVar(n[1])
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc globalVarInitCheck(c: PContext, n: PNode) =
|
||||
if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
|
||||
if usesLocalVar(n):
|
||||
localError(c.config, n.info, errCannotAssignToGlobal)
|
||||
|
||||
const
|
||||
@@ -768,7 +780,7 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
|
||||
# use same symkind for compatibility with original section
|
||||
let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
|
||||
temp.typ = typ
|
||||
temp.flags.incl(sfGenSym)
|
||||
temp.flagsImpl.incl(sfGenSym)
|
||||
lastDef = newNodeI(defkind, a.info)
|
||||
newSons(lastDef, 3)
|
||||
lastDef[0] = newSymNode(temp)
|
||||
@@ -926,11 +938,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
else:
|
||||
if v.owner == nil: setOwner(v, c.p.owner)
|
||||
when oKeepVariableNames:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
if c.inUnrolledContext > 0: v.incl(sfShadowed)
|
||||
else:
|
||||
let shadowed = findShadowedVar(c, v)
|
||||
if shadowed != nil:
|
||||
shadowed.flags.incl(sfShadowed)
|
||||
shadowed.incl(sfShadowed)
|
||||
if shadowed.kind == skResult and sfGenSym notin v.flags:
|
||||
message(c.config, a.info, warnResultShadowed)
|
||||
if def.kind != nkEmpty:
|
||||
@@ -1102,13 +1114,13 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
|
||||
for i in 0..<n[0].len-1:
|
||||
var v = symForVar(c, n[0][i])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
if getCurrOwner(c).kind == skModule: incl(v, sfGlobal)
|
||||
case iter.kind
|
||||
of tyVar, tyLent:
|
||||
v.typ = newTypeS(iter.kind, c)
|
||||
v.typ.add iterAfterVarLent[i]
|
||||
if tfVarIsPtr in iter.flags:
|
||||
v.typ.flags.incl tfVarIsPtr
|
||||
v.typ.incl tfVarIsPtr
|
||||
else:
|
||||
v.typ = iter[i]
|
||||
n[0][i] = newSymNode(v)
|
||||
@@ -1116,7 +1128,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
else:
|
||||
var v = symForVar(c, n[0])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
if getCurrOwner(c).kind == skModule: incl(v, sfGlobal)
|
||||
# BUGFIX: don't use `iter` here as that would strip away
|
||||
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
||||
# for an example:
|
||||
@@ -1146,7 +1158,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
localError(c.config, n[i].info, errWrongNumberOfVariables)
|
||||
for j in 0..<n[i].len-1:
|
||||
var v = symForVar(c, n[i][j])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
if getCurrOwner(c).kind == skModule: incl(v, sfGlobal)
|
||||
if mutable:
|
||||
v.typ = newTypeS(tyVar, c)
|
||||
v.typ.add iter[i][j]
|
||||
@@ -1160,13 +1172,13 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
elif v.owner == nil: setOwner(v, getCurrOwner(c))
|
||||
else:
|
||||
var v = symForVar(c, n[i])
|
||||
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
|
||||
if getCurrOwner(c).kind == skModule: incl(v, sfGlobal)
|
||||
case iter.kind
|
||||
of tyVar, tyLent:
|
||||
v.typ = newTypeS(iter.kind, c)
|
||||
v.typ.add iterAfterVarLent[i]
|
||||
if tfVarIsPtr in iter.flags:
|
||||
v.typ.flags.incl tfVarIsPtr
|
||||
v.typ.incl tfVarIsPtr
|
||||
else:
|
||||
v.typ = iter[i]
|
||||
n[i] = newSymNode(v)
|
||||
@@ -1447,7 +1459,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||
onDef(name[1].info, s)
|
||||
s.typ = newTypeS(tyObject, c)
|
||||
s.typ.sym = s
|
||||
s.flags.incl sfForward
|
||||
s.incl sfForward
|
||||
c.graph.packageTypes.strTableAdd s
|
||||
addInterfaceDecl(c, s)
|
||||
elif typsym.kind == skType and sfForward in typsym.flags:
|
||||
@@ -1460,8 +1472,13 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
|
||||
else:
|
||||
s = semIdentDef(c, name, skType)
|
||||
onDef(name.info, s)
|
||||
s.typ = newTypeS(tyForward, c)
|
||||
s.typ.sym = s # process pragmas:
|
||||
if s.typ != nil:
|
||||
# name node is a symbol with a type already, probably in resem, don't touch it
|
||||
discard
|
||||
else:
|
||||
s.typ = newTypeS(tyForward, c)
|
||||
s.typ.sym = s
|
||||
# process pragmas:
|
||||
if name.kind == nkPragmaExpr:
|
||||
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
|
||||
if rewritten != nil:
|
||||
@@ -1533,7 +1550,7 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
|
||||
|
||||
case t.kind
|
||||
of tyGenericParam:
|
||||
t.flags.incl tfWeakCovariant
|
||||
t.incl tfWeakCovariant
|
||||
return true
|
||||
of tyObject:
|
||||
for field in t.n:
|
||||
@@ -1559,7 +1576,7 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
|
||||
error("covariant param '" & param.sym.name.s &
|
||||
"' used in a non-covariant position")
|
||||
elif tfWeakCovariant in formalFlags:
|
||||
param.flags.incl tfWeakCovariant
|
||||
param.incl tfWeakCovariant
|
||||
result = true
|
||||
elif tfContravariant in param.flags:
|
||||
let formalParam = targetBody[i-1].sym
|
||||
@@ -1599,7 +1616,26 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
localError(c.config, a.info, errImplOfXexpected % s.name.s)
|
||||
if s.magic != mNone: processMagicType(c, s)
|
||||
let oldFlags = s.typ.flags
|
||||
if a[1].kind != nkEmpty:
|
||||
let preserveSym = s.typ != nil and s.typ.kind != tyForward and sfForward notin s.flags and
|
||||
s.magic == mNone # magic might have received type above but still needs processing
|
||||
if preserveSym:
|
||||
# symbol already has a type, probably in resem, do not modify it
|
||||
# but still semcheck the RHS to handle any defined symbols
|
||||
# nominal type nodes are still ignored in semtypes
|
||||
if a[1].kind != nkEmpty:
|
||||
openScope(c)
|
||||
pushOwner(c, s)
|
||||
a[1] = semGenericParamList(c, a[1], nil)
|
||||
inc c.inGenericContext
|
||||
discard semTypeNode(c, a[2], s.typ)
|
||||
dec c.inGenericContext
|
||||
popOwner(c)
|
||||
closeScope(c)
|
||||
elif a[2].kind != nkEmpty:
|
||||
pushOwner(c, s)
|
||||
discard semTypeNode(c, a[2], s.typ)
|
||||
popOwner(c)
|
||||
elif a[1].kind != nkEmpty:
|
||||
# We have a generic type declaration here. In generic types,
|
||||
# symbol lookup needs to be done here.
|
||||
openScope(c)
|
||||
@@ -1632,11 +1668,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
body.size = -1 # could not be computed properly
|
||||
if body.kind == tyObject:
|
||||
# add flags applied to generic type to object (nominal) type
|
||||
incl(body.flags, oldFlags)
|
||||
incl(body, oldFlags)
|
||||
# {.inheritable, final.} is already disallowed, but
|
||||
# object might have been assumed to be final
|
||||
if tfInheritable in oldFlags and tfFinal in body.flags:
|
||||
excl(body.flags, tfFinal)
|
||||
excl(body, tfFinal)
|
||||
s.typ[^1] = body
|
||||
if tfCovariant in s.typ.flags:
|
||||
checkCovariantParamsUsages(c, s.typ)
|
||||
@@ -1685,27 +1721,27 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
# flag might be copied from alias/instantiation:
|
||||
let t = body.skipTypes({tyAlias, tyGenericInst})
|
||||
if not (t.kind == tyDistinct and tfBorrowDot in t.flags):
|
||||
excl s.typ.flags, tfBorrowDot
|
||||
excl s.typ, tfBorrowDot
|
||||
localError(c.config, name.info, "only a 'distinct' type can borrow `.`")
|
||||
let aa = a[2]
|
||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||
aa[0].kind == nkObjectTy:
|
||||
aa[0].kind == nkObjectTy and not preserveSym:
|
||||
# give anonymous object a dummy symbol:
|
||||
var st = s.typ
|
||||
if st.kind == tyGenericBody: st = st.typeBodyImpl
|
||||
internalAssert c.config, st.kind in {tyPtr, tyRef}
|
||||
internalAssert c.config, st.last.sym == nil
|
||||
incl st.flags, tfRefsAnonObj
|
||||
incl st, tfRefsAnonObj
|
||||
let objTy = st.last
|
||||
# add flags for `ref object` etc to underlying `object`
|
||||
incl(objTy.flags, oldFlags)
|
||||
incl(objTy, oldFlags)
|
||||
# {.inheritable, final.} is already disallowed, but
|
||||
# object might have been assumed to be final
|
||||
if tfInheritable in oldFlags and tfFinal in objTy.flags:
|
||||
excl(objTy.flags, tfFinal)
|
||||
excl(objTy, tfFinal)
|
||||
let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
|
||||
c.idgen, getCurrOwner(c), s.info)
|
||||
obj.flags.incl sfGeneratedType
|
||||
obj.flagsImpl.incl sfGeneratedType
|
||||
let symNode = newSymNode(obj)
|
||||
obj.ast = a.shallowCopy
|
||||
case a[0].kind
|
||||
@@ -1727,12 +1763,9 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
obj.ast[1] = a[1]
|
||||
obj.ast[2] = a[2][0]
|
||||
if sfPure in s.flags:
|
||||
obj.flags.incl sfPure
|
||||
obj.incl sfPure
|
||||
obj.typ = objTy
|
||||
objTy.sym = obj
|
||||
for sk in c.skipTypes:
|
||||
discard semTypeNode(c, sk, nil)
|
||||
c.skipTypes = @[]
|
||||
|
||||
proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
||||
proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) =
|
||||
@@ -1768,6 +1801,15 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
||||
internalAssert c.config, false
|
||||
|
||||
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
for (typ, typeNode) in c.forwardTypeUpdates:
|
||||
# types that need to be updated due to containing forward types
|
||||
# and their corresponding type nodes
|
||||
# for example generic invocations of forward types end up here
|
||||
var reified = semTypeNode(c, typeNode, nil)
|
||||
assert reified != nil
|
||||
assignType(typ, reified)
|
||||
typ.itemId = reified.itemId # same id
|
||||
c.forwardTypeUpdates = @[]
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
if a.kind == nkCommentStmt: continue
|
||||
@@ -1794,29 +1836,15 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
else:
|
||||
while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
|
||||
x = x.lastSon
|
||||
# we need the 'safeSkipTypes' here because illegally recursive types
|
||||
# can enter at this point, see bug #13763
|
||||
if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
|
||||
s.typ.safeSkipTypes(abstractPtrs).kind notin {tyObject, tyEnum}:
|
||||
# type aliases are hard:
|
||||
var t = semTypeNode(c, x, nil)
|
||||
assert t != nil
|
||||
if s.typ != nil and s.typ.kind notin {tyAlias, tySink}:
|
||||
if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
|
||||
assignType(s.typ, t)
|
||||
s.typ.itemId = t.itemId
|
||||
elif t.kind in {tyObject, tyEnum, tyDistinct}:
|
||||
assert s.typ != nil
|
||||
assignType(s.typ, t)
|
||||
s.typ.itemId = t.itemId # same id
|
||||
var hasError = false
|
||||
let baseType = s.typ.safeSkipTypes(abstractPtrs)
|
||||
if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and
|
||||
(x.kind in {nkObjectTy, nkTupleTy} or
|
||||
if x.kind in {nkObjectTy, nkTupleTy} or
|
||||
(x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and
|
||||
x[0].kind in {nkObjectTy, nkTupleTy})
|
||||
):
|
||||
checkForMetaFields(c, baseType.n, hasError)
|
||||
x[0].kind in {nkObjectTy, nkTupleTy}):
|
||||
# we need the 'safeSkipTypes' here because illegally recursive types
|
||||
# can enter at this point, see bug #13763
|
||||
let baseType = s.typ.safeSkipTypes(abstractPtrs)
|
||||
if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil:
|
||||
checkForMetaFields(c, baseType.n, hasError)
|
||||
if not hasError:
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
#instAllTypeBoundOp(c, n.info)
|
||||
@@ -1926,7 +1954,7 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen,
|
||||
getCurrOwner(c), n.info)
|
||||
s.typ = t
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
|
||||
if owner == skMacro or t != nil:
|
||||
if n.len > resultPos and n[resultPos] != nil:
|
||||
@@ -2102,7 +2130,7 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
if cond:
|
||||
var obj = t.firstParamType
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
incl(obj, tfHasAsgn)
|
||||
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
@@ -2131,19 +2159,23 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=dup' must be proc[T: object](x: T): T")
|
||||
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
incl(s, sfOverridden)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
template notRefc: bool =
|
||||
# fixes refc with non-var destructor; cancel warnings (#23156)
|
||||
c.config.backend == backendJs or
|
||||
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}
|
||||
let cond = case op
|
||||
of attachedWasMoved:
|
||||
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
|
||||
of attachedTrace:
|
||||
t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer
|
||||
of attachedDestructor:
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
if notRefc:
|
||||
t.len == 2 and t.returnType == nil
|
||||
else:
|
||||
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
|
||||
@@ -2153,14 +2185,11 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
if cond:
|
||||
var obj = t.firstParamType.skipTypes({tyVar})
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
incl(obj, tfHasAsgn)
|
||||
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
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:
|
||||
@@ -2179,7 +2208,7 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
|
||||
of attachedDestructor:
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
if notRefc:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
|
||||
else:
|
||||
@@ -2188,8 +2217,8 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
incl(s, sfOverridden)
|
||||
|
||||
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
let name = s.name.s.normalize
|
||||
@@ -2229,19 +2258,19 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
incl(s, sfOverridden)
|
||||
of "=", "=copy", "=sink":
|
||||
if s.magic == mAsgn: return
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverridden)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
incl(s, sfOverridden)
|
||||
if name == "=":
|
||||
message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead")
|
||||
let t = s.typ
|
||||
if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar:
|
||||
var obj = t.firstParamType.elementType
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
incl(obj, tfHasAsgn)
|
||||
if obj.kind == tyGenericBody: obj = obj.skipModifier
|
||||
elif obj.kind == tyGenericInvocation: obj = obj.genericHead
|
||||
else: break
|
||||
@@ -2400,8 +2429,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
case n[namePos].kind
|
||||
of nkEmpty:
|
||||
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
|
||||
s.flags.incl sfUsed
|
||||
s.flags.incl sfGenSym
|
||||
s.flagsImpl.incl sfUsed
|
||||
s.incl sfGenSym
|
||||
n[namePos] = newSymNode(s)
|
||||
of nkSym:
|
||||
s = n[namePos].sym
|
||||
@@ -2427,7 +2456,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
#s.scope = c.currentScope
|
||||
if s.kind in {skMacro, skTemplate}:
|
||||
# push noalias flag at first to prevent unwanted recursive calls:
|
||||
incl(s.flags, sfNoalias)
|
||||
incl(s, sfNoalias)
|
||||
|
||||
# before compiling the proc params & body, set as current the scope
|
||||
# where the proc was declared
|
||||
@@ -2465,14 +2494,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
n[genericParamsPos] = n[miscPos][1]
|
||||
n[miscPos] = c.graph.emptyNode
|
||||
|
||||
if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime)
|
||||
if tfTriggersCompileTime in s.typ.flags: incl(s, sfCompileTime)
|
||||
if n[patternPos].kind != nkEmpty:
|
||||
n[patternPos] = semPattern(c, n[patternPos], s)
|
||||
if s.kind == skIterator:
|
||||
s.typ.flags.incl(tfIterator)
|
||||
s.typ.incl(tfIterator)
|
||||
elif s.kind == skFunc:
|
||||
incl(s.flags, sfNoSideEffect)
|
||||
incl(s.typ.flags, tfNoSideEffect)
|
||||
incl(s, sfNoSideEffect)
|
||||
incl(s.typ, tfNoSideEffect)
|
||||
|
||||
var (proto, comesFromShadowScope) =
|
||||
if isAnon: (nil, false)
|
||||
@@ -2518,7 +2547,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
|
||||
if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags:
|
||||
setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s)
|
||||
s.typ.flags.incl tfEffectSystemWorkaround
|
||||
s.typ.incl tfEffectSystemWorkaround
|
||||
|
||||
# To ease macro generation that produce forwarded .async procs we now
|
||||
# allow a bit redundancy in the pragma declarations. The rule is
|
||||
@@ -2545,8 +2574,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
if sfForward notin proto.flags and proto.magic == mNone:
|
||||
wrongRedefinition(c, n.info, proto.name.s, proto.info)
|
||||
if not comesFromShadowScope:
|
||||
excl(proto.flags, sfForward)
|
||||
incl(proto.flags, sfWasForwarded)
|
||||
excl(proto, sfForward)
|
||||
incl(proto, sfWasForwarded)
|
||||
suggestSym(c.graph, s.info, proto, c.graph.usageSym)
|
||||
closeScope(c) # close scope with wrong parameter symbols
|
||||
openScope(c) # open scope for old (correct) parameter symbols
|
||||
@@ -2616,9 +2645,18 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
else:
|
||||
nil
|
||||
# semantic checking also needed with importc in case used in VM
|
||||
|
||||
let isInlineIterator = isInlineIterator(s.typ)
|
||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
|
||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||
# context as it may even be evaluated in 'system.compiles':
|
||||
|
||||
if isInlineIterator and s.typ.callConv == ccClosure:
|
||||
# iterators without explicit callconvs are lifted to closure,
|
||||
# we need to add a result symbol for them
|
||||
maybeAddResult(c, s, n)
|
||||
|
||||
|
||||
trackProc(c, s, s.ast[bodyPos])
|
||||
else:
|
||||
if (s.typ.returnType != nil and s.kind != skIterator):
|
||||
@@ -2639,8 +2677,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
if s.kind in {skProc, skFunc} and s.typ.returnType != nil and s.typ.returnType.kind == tyAnything:
|
||||
localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations")
|
||||
|
||||
incl(s.flags, sfForward)
|
||||
incl(s.flags, sfWasForwarded)
|
||||
incl(s, sfForward)
|
||||
incl(s, sfWasForwarded)
|
||||
elif sfBorrow in s.flags: semBorrow(c, n, s)
|
||||
sideEffectsCheck(c, s)
|
||||
|
||||
@@ -2687,7 +2725,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
# we require first class iterators to be marked with 'closure' explicitly
|
||||
# -- at least for 0.9.2.
|
||||
if s.typ.callConv == ccClosure:
|
||||
incl(s.typ.flags, tfCapturesEnv)
|
||||
incl(s.typ, tfCapturesEnv)
|
||||
else:
|
||||
s.typ.callConv = ccInline
|
||||
if result[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0:
|
||||
@@ -2757,14 +2795,14 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
|
||||
if param.typ.kind != tyUntyped: allUntyped = false
|
||||
# no default value, parameters required in call
|
||||
if param.ast == nil: nullary = false
|
||||
if allUntyped: incl(s.flags, sfAllUntyped)
|
||||
if allUntyped: incl(s, sfAllUntyped)
|
||||
if nullary and n[genericParamsPos].kind == nkEmpty:
|
||||
# macro can be called with alias syntax, remove pushed noalias flag
|
||||
excl(s.flags, sfNoalias)
|
||||
excl(s, sfNoalias)
|
||||
if n[bodyPos].kind == nkEmpty:
|
||||
localError(c.config, n.info, errImplOfXexpected % s.name.s)
|
||||
|
||||
proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
|
||||
proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult, resolvedIncStmt: PNode) =
|
||||
var f = checkModuleName(c.config, it)
|
||||
if f != InvalidFileIdx:
|
||||
addIncludeFileDep(c, f)
|
||||
@@ -2772,12 +2810,22 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
|
||||
if containsOrIncl(c.includedFiles, f.int):
|
||||
localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f))
|
||||
else:
|
||||
if resolvedIncStmt != nil:
|
||||
resolvedIncStmt.add newStrNode(toFullPath(c.config, f), it.info)
|
||||
includeStmtResult.add semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f), {})
|
||||
excl(c.includedFiles, f.int)
|
||||
|
||||
proc evalInclude(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
result.add n
|
||||
var resolvedIncStmt: PNode = nil
|
||||
if optCompress in c.config.globalOptions:
|
||||
# New resolve the include filenames to string literals that contain absolute paths,
|
||||
# nicer for IC:
|
||||
resolvedIncStmt = newNodeI(nkIncludeStmt, n.info)
|
||||
result.add resolvedIncStmt
|
||||
else:
|
||||
# Legacy: Keep `include` statement as is:
|
||||
result.add n
|
||||
template checkAs(it: PNode) =
|
||||
if it.kind == nkInfix and it.len == 3:
|
||||
let op = it[0].getPIdent
|
||||
@@ -2795,18 +2843,33 @@ proc evalInclude(c: PContext, n: PNode): PNode =
|
||||
for x in it[lastPos]:
|
||||
checkAs(x)
|
||||
imp[lastPos] = x
|
||||
incMod(c, n, imp, result)
|
||||
incMod(c, n, imp, result, resolvedIncStmt)
|
||||
else:
|
||||
incMod(c, n, it, result)
|
||||
incMod(c, n, it, result, resolvedIncStmt)
|
||||
|
||||
proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
|
||||
if n != nil:
|
||||
for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
|
||||
incl(n.flags, flag)
|
||||
|
||||
proc enterPragmaBlock(c: PContext): POptionEntry =
|
||||
result = POptionEntry(options: c.config.options,
|
||||
notes: c.config.notes,
|
||||
warningAsErrors: c.config.warningAsErrors,
|
||||
features: c.features)
|
||||
|
||||
proc leavePragmaBlock(c: PContext, p: POptionEntry) =
|
||||
c.config.options = p.options
|
||||
c.config.notes = p.notes
|
||||
c.config.warningAsErrors = p.warningAsErrors
|
||||
c.features = p.features
|
||||
|
||||
proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
checkSonsLen(n, 2, c.config)
|
||||
let pragmaList = n[0]
|
||||
|
||||
let oldOptionEntry = enterPragmaBlock(c)
|
||||
|
||||
pragma(c, nil, pragmaList, exprPragmas, isStatement = true)
|
||||
|
||||
var inUncheckedAssignSection = 0
|
||||
@@ -2831,6 +2894,8 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
|
||||
else: discard
|
||||
|
||||
leavePragmaBlock(c, oldOptionEntry)
|
||||
|
||||
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||
#echo "semStaticStmt"
|
||||
#writeStackTrace()
|
||||
|
||||
@@ -67,12 +67,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
# for instance 'nextTry' is both in tables.nim and astalgo.nim ...
|
||||
if not isField or sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
if isField:
|
||||
# possibly not final field sym
|
||||
incl(s.flags, sfUsed)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
else:
|
||||
markUsed(c, info, s)
|
||||
# possibly not final field sym
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -88,7 +85,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
a = initOverloadIter(o, c, n)
|
||||
while a != nil:
|
||||
if a.kind != skModule and (not isField or sfGenSym notin a.flags):
|
||||
incl(a.flags, sfUsed)
|
||||
incl(a.flagsImpl, sfUsed)
|
||||
markOwnerModuleAsUsed(c, a)
|
||||
result.add newSymNode(a, info)
|
||||
onUse(info, a)
|
||||
@@ -183,8 +180,7 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
|
||||
|
||||
proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info)
|
||||
incl(result.flags, sfGenSym)
|
||||
incl(result.flags, sfShadowed)
|
||||
incl(result.flagsImpl, {sfGenSym, sfShadowed})
|
||||
|
||||
proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
# locals default to 'gensym', fields default to 'inject':
|
||||
@@ -221,10 +217,10 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
onDef(n.info, local)
|
||||
replaceIdentBySym(c.c, n, newSymNode(local, n.info))
|
||||
if k == skParam and c.inTemplateHeader > 0:
|
||||
local.flags.incl sfTemplateParam
|
||||
local.incl sfTemplateParam
|
||||
|
||||
proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, 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:
|
||||
@@ -301,7 +297,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNo
|
||||
if s != nil:
|
||||
if s.owner == c.owner and (s.kind == skParam or
|
||||
(sfGenSym in s.flags and not explicitInject)):
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
@@ -387,7 +383,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
elif contains(c.toBind, s.id):
|
||||
@@ -397,7 +393,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0:
|
||||
# template tmp[T](x: var seq[T]) =
|
||||
# var yz: T
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
@@ -611,7 +607,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
# do not symchoice a quoted template parameter (bug #2390):
|
||||
if s.owner == c.owner and s.kind == skParam and
|
||||
n.kind == nkAccQuoted and n.len == 1:
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
onUse(n.info, s)
|
||||
return newSymNode(s, n.info)
|
||||
elif contains(c.toBind, s.id):
|
||||
@@ -691,7 +687,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
var s: PSym
|
||||
if isTopLevel(c):
|
||||
s = semIdentVis(c, skTemplate, n[namePos], {sfExported})
|
||||
incl(s.flags, sfGlobal)
|
||||
incl(s, sfGlobal)
|
||||
else:
|
||||
s = semIdentVis(c, skTemplate, n[namePos], {})
|
||||
assert s.kind == skTemplate
|
||||
@@ -704,7 +700,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
# check parameter list:
|
||||
#s.scope = c.currentScope
|
||||
# push noalias flag at first to prevent unwanted recursive calls:
|
||||
incl(s.flags, sfNoalias)
|
||||
incl(s, sfNoalias)
|
||||
pushOwner(c, s)
|
||||
openScope(c)
|
||||
n[namePos] = newSymNode(s)
|
||||
@@ -727,8 +723,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
for i in 1..<s.typ.n.len:
|
||||
let param = s.typ.n[i].sym
|
||||
if param.name.id != ord(wUnderscore):
|
||||
param.flags.incl sfTemplateParam
|
||||
param.flags.excl sfGenSym
|
||||
param.incl sfTemplateParam
|
||||
param.excl sfGenSym
|
||||
if param.typ.kind != tyUntyped: allUntyped = false
|
||||
# no default value, parameters required in call
|
||||
if param.ast == nil: nullary = false
|
||||
@@ -742,12 +738,12 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
# restore original generic type params as no explicit or implicit were found
|
||||
n[genericParamsPos] = n[miscPos][1]
|
||||
n[miscPos] = c.graph.emptyNode
|
||||
if allUntyped: incl(s.flags, sfAllUntyped)
|
||||
if allUntyped: incl(s, sfAllUntyped)
|
||||
if nullary and
|
||||
n[genericParamsPos].kind == nkEmpty and
|
||||
n[bodyPos].kind != nkEmpty:
|
||||
# template can be called with alias syntax, remove pushed noalias flag
|
||||
excl(s.flags, sfNoalias)
|
||||
excl(s, sfNoalias)
|
||||
|
||||
if n[patternPos].kind != nkEmpty:
|
||||
n[patternPos] = semPattern(c, n[patternPos], s)
|
||||
@@ -804,7 +800,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
|
||||
# macros because they have a shadowed param of type 'PNimNode' (see
|
||||
# semtypes.addParamOrResult). Within the pattern we have to ensure
|
||||
# to use the param with the proper type though:
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
onUse(n.info, s)
|
||||
let x = c.owner.typ.n[s.position+1].sym
|
||||
assert x.name == s.name
|
||||
|
||||
@@ -38,32 +38,58 @@ const
|
||||
errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
|
||||
errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
|
||||
|
||||
proc reusePrev(prev: PType): bool {.inline.} =
|
||||
# only overwrite `prev` if it is a forward type, partial object or magic type
|
||||
result = prev != nil and (prev.kind == tyForward or (prev.sym != nil and
|
||||
# partial object marks sym as `sfForward`
|
||||
(sfForward in prev.sym.flags or prev.sym.magic != mNone)))
|
||||
|
||||
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, son: sink PType): PType =
|
||||
if prev == nil or prev.kind == tyGenericBody:
|
||||
result = newTypeS(kind, c, son)
|
||||
else:
|
||||
if reusePrev(prev):
|
||||
result = prev
|
||||
result.setSon(son)
|
||||
if result.kind == tyForward: result.kind = kind
|
||||
else:
|
||||
result = newTypeS(kind, c, son)
|
||||
#if kind == tyError: result.flags.incl tfCheckedForDestructor
|
||||
|
||||
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
|
||||
if prev == nil or prev.kind == tyGenericBody:
|
||||
result = newTypeS(kind, c)
|
||||
else:
|
||||
if reusePrev(prev):
|
||||
result = prev
|
||||
if result.kind == tyForward: result.kind = kind
|
||||
else:
|
||||
result = newTypeS(kind, c)
|
||||
|
||||
proc newConstraint(c: PContext, k: TTypeKind): PType =
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
result.flags.incl tfCheckedForDestructor
|
||||
result.incl tfCheckedForDestructor
|
||||
result.addSonSkipIntLit(newTypeS(k, c), c.idgen)
|
||||
|
||||
proc skipGenericPrev(prev: PType): PType =
|
||||
result = prev
|
||||
if prev.kind == tyGenericBody and prev.last.kind != tyNone:
|
||||
result = prev.last
|
||||
|
||||
proc prevIsKind(prev: PType, kind: TTypeKind): bool {.inline.} =
|
||||
result = prev != nil and skipGenericPrev(prev).kind == kind
|
||||
|
||||
proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n.len == 0: return newConstraint(c, tyEnum)
|
||||
elif n.len == 1:
|
||||
# don't create an empty tyEnum; fixes #3052
|
||||
return errorType(c)
|
||||
if prevIsKind(prev, tyEnum):
|
||||
# the symbol already has an enum type (likely resem), don't define a new enum
|
||||
# but add the enum fields to scope from the original type
|
||||
let isPure = sfPure in prev.sym.flags
|
||||
for enumField in prev.n:
|
||||
assert enumField.kind == nkSym
|
||||
let e = enumField.sym
|
||||
if not isPure:
|
||||
addInterfaceOverloadableSymAt(c, c.currentScope, e)
|
||||
else:
|
||||
declarePureEnumField(c, e)
|
||||
return prev
|
||||
var
|
||||
counter, x: BiggestInt = 0
|
||||
e: PSym = nil
|
||||
@@ -125,7 +151,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
if i != 1:
|
||||
if x != counter:
|
||||
needsReorder = true
|
||||
incl(result.flags, tfEnumHasHoles)
|
||||
incl(result, tfEnumHasHoles)
|
||||
e.ast = strVal # might be nil
|
||||
counter = x
|
||||
of nkSym:
|
||||
@@ -158,7 +184,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
identToReplace[] = symNode
|
||||
if e.position == 0: hasNull = true
|
||||
if result.sym != nil and sfExported in result.sym.flags:
|
||||
e.flags.incl {sfUsed, sfExported}
|
||||
e.incl {sfUsed, sfExported}
|
||||
|
||||
result.n.add symNode
|
||||
styleCheckDef(c, e)
|
||||
@@ -186,7 +212,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
if isPure and sfExported in result.sym.flags:
|
||||
addPureEnum(c, LazySym(sym: result.sym))
|
||||
if tfNotNil in e.typ.flags and not hasNull:
|
||||
result.flags.incl tfRequiresInit
|
||||
result.incl tfRequiresInit
|
||||
setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen))
|
||||
|
||||
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||
@@ -197,7 +223,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
|
||||
if base.kind notin {tyGenericParam, tyGenericInvocation}:
|
||||
if base.kind == tyForward:
|
||||
c.skipTypes.add n
|
||||
c.forwardTypeUpdates.add (base, n[1])
|
||||
elif not isOrdinalType(base, allowEnumWithHoles = true):
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
|
||||
elif lengthOrd(c.config, base) > MaxSetElements:
|
||||
@@ -307,6 +333,9 @@ proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdG
|
||||
|
||||
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n.len == 0: return newConstraint(c, tyDistinct)
|
||||
if prevIsKind(prev, tyDistinct):
|
||||
# the symbol already has a distinct type (likely resem), don't create a new type
|
||||
return skipGenericPrev(prev)
|
||||
result = newOrPrevType(tyDistinct, prev, c)
|
||||
addSonSkipIntLitChecked(c, result, semTypeNode(c, n[0], nil), n[0], c.idgen)
|
||||
if n.len > 1: result.n = n[1]
|
||||
@@ -349,7 +378,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
for i in 0..1:
|
||||
if hasUnresolvedArgs(c, range[i]):
|
||||
result.n.add makeStaticExpr(c, range[i])
|
||||
result.flags.incl tfUnresolved
|
||||
result.incl tfUnresolved
|
||||
else:
|
||||
result.n.add semConstExpr(c, range[i])
|
||||
|
||||
@@ -369,15 +398,15 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||
if not isDefined(c.config, "nimPreviewRangeDefault"):
|
||||
let n = result.n
|
||||
if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
|
||||
incl(result.flags, tfRequiresInit)
|
||||
incl(result, tfRequiresInit)
|
||||
elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
|
||||
incl(result.flags, tfRequiresInit)
|
||||
incl(result, tfRequiresInit)
|
||||
elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
|
||||
n[0].floatVal > 0.0:
|
||||
incl(result.flags, tfRequiresInit)
|
||||
incl(result, tfRequiresInit)
|
||||
elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
|
||||
n[1].floatVal < 0.0:
|
||||
incl(result.flags, tfRequiresInit)
|
||||
incl(result, tfRequiresInit)
|
||||
else:
|
||||
if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
|
||||
localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
|
||||
@@ -424,10 +453,10 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
let info = if n.safeLen > 1: n[1].info else: n.info
|
||||
localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
if c.inGenericContext > 0: result.incl tfUnresolved
|
||||
else:
|
||||
result = e.typ.skipTypes({tyTypeDesc})
|
||||
result.flags.incl tfImplicitStatic
|
||||
result.incl tfImplicitStatic
|
||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
|
||||
if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
|
||||
@@ -527,7 +556,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
typ = a[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, a[^1], typ)
|
||||
typ = a[^1].typ
|
||||
typ = a[^1].typ.skipIntLit(c.idgen)
|
||||
elif a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||
@@ -553,7 +582,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
styleCheckDef(c, a[j].info, field)
|
||||
onDef(field.info, field)
|
||||
if result.n.len == 0: result.n = nil
|
||||
if isTupleRecursive(result):
|
||||
if isRecursiveStructuralType(result):
|
||||
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result))
|
||||
|
||||
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
@@ -566,7 +595,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
|
||||
result = newSymG(kind, n[1], c)
|
||||
var v = considerQuotedIdent(c, n[0])
|
||||
if sfExported in allowed and v.id == ord(wStar):
|
||||
incl(result.flags, sfExported)
|
||||
incl(result, sfExported)
|
||||
else:
|
||||
if not (sfExported in allowed):
|
||||
localError(c.config, n[0].info, errXOnlyAtModuleScope % "export")
|
||||
@@ -776,7 +805,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
if a[0].kind != nkSym:
|
||||
internalError(c.config, "semRecordCase: discriminant is no symbol")
|
||||
return
|
||||
incl(a[0].sym.flags, sfDiscriminant)
|
||||
incl(a[0].sym, sfDiscriminant)
|
||||
var covered = toInt128(0)
|
||||
var chckCovered = false
|
||||
var typ = skipTypes(a[0].typ, abstractVar-{tyTypeDesc})
|
||||
@@ -784,7 +813,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
case typ.kind
|
||||
of shouldChckCovered:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyError:
|
||||
of tyError:
|
||||
discard
|
||||
of tyRange:
|
||||
if skipTypes(typ.elementType, abstractInst).kind in shouldChckCovered:
|
||||
@@ -792,7 +821,8 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
of tyForward:
|
||||
errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
|
||||
elif not isOrdinalType(typ):
|
||||
localError(c.config, n[0].info, "selector must be of an ordinal type, float")
|
||||
localError(c.config, n[0].info, "selector must be of an ordinal type")
|
||||
|
||||
if firstOrd(c.config, typ) != 0:
|
||||
localError(c.config, n.info, "low(" & $a[0].sym.name.s &
|
||||
") must be 0 for discriminant")
|
||||
@@ -898,7 +928,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
typ = n[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, n[^1], typ)
|
||||
typ = n[^1].typ
|
||||
typ = n[^1].typ.skipIntLit(c.idgen)
|
||||
propagateToOwner(rectype, typ)
|
||||
elif n[^2].kind == nkEmpty:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
@@ -918,14 +948,19 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
else:
|
||||
n[i].info
|
||||
suggestSym(c.graph, info, f, c.graph.usageSym)
|
||||
# this must only be enabled for cmd == cmdNif as its a minor breaking
|
||||
# change otherwise :-(
|
||||
if c.config.cmd == cmdCompileToNif:
|
||||
n[i] = newSymNode(f)
|
||||
f.typ = typ
|
||||
f.position = pos
|
||||
f.options = c.config.options
|
||||
if fieldOwner != nil and
|
||||
{sfImportc, sfExportc} * fieldOwner.flags != {} and
|
||||
not hasCaseFields and f.loc.snippet == "":
|
||||
f.loc.snippet = rope(f.name.s)
|
||||
f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
|
||||
ensureMutable f
|
||||
f.locImpl.snippet = rope(f.name.s)
|
||||
f.incl {sfImportc, sfExportc} * fieldOwner.flags
|
||||
inc(pos)
|
||||
if containsOrIncl(check, f.name.id):
|
||||
localError(c.config, info, "attempt to redefine: '" & f.name.s & "'")
|
||||
@@ -994,11 +1029,15 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
result = nil
|
||||
if n.len == 0:
|
||||
return newConstraint(c, tyObject)
|
||||
if prevIsKind(prev, tyObject) and sfForward notin prev.sym.flags:
|
||||
# the symbol already has an object type (likely resem), don't create a new type
|
||||
return skipGenericPrev(prev)
|
||||
var check = initIntSet()
|
||||
var pos = 0
|
||||
var base, realBase: PType = nil
|
||||
# n[0] contains the pragmas (if any). We process these later...
|
||||
checkSonsLen(n, 3, c.config)
|
||||
var needsForwardUpdate = false
|
||||
if n[1].kind != nkEmpty:
|
||||
realBase = semTypeNode(c, n[1][0], nil)
|
||||
base = skipTypesOrNil(realBase, skipPtrs)
|
||||
@@ -1020,7 +1059,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
return newType(tyError, c.idgen, result.owner)
|
||||
|
||||
elif concreteBase.kind == tyForward:
|
||||
c.skipTypes.add n #we retry in the final pass
|
||||
needsForwardUpdate = true
|
||||
else:
|
||||
if concreteBase.kind != tyError:
|
||||
localError(c.config, n[1].info, "inheritance only works with non-final objects; " &
|
||||
@@ -1030,10 +1069,14 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
realBase = nil
|
||||
if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode")
|
||||
result = newOrPrevType(tyObject, prev, c)
|
||||
if needsForwardUpdate:
|
||||
# if the inherited object is a forward type,
|
||||
# the entire object needs to be checked again
|
||||
c.forwardTypeUpdates.add (result, n) # we retry in the final pass
|
||||
rawAddSon(result, realBase)
|
||||
if realBase == nil and tfInheritable in flags:
|
||||
result.flags.incl tfInheritable
|
||||
if tfAcyclic in flags: result.flags.incl tfAcyclic
|
||||
result.incl tfInheritable
|
||||
if tfAcyclic in flags: result.incl tfAcyclic
|
||||
if result.n.isNil:
|
||||
result.n = newNodeI(nkRecList, n.info)
|
||||
else:
|
||||
@@ -1048,14 +1091,17 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
s.typ = result
|
||||
pragma(c, s, n[0], typePragmas)
|
||||
if base == nil and tfInheritable notin result.flags:
|
||||
incl(result.flags, tfFinal)
|
||||
incl(result, tfFinal)
|
||||
if c.inGenericContext == 0 and computeRequiresInit(c, result):
|
||||
result.flags.incl tfRequiresInit
|
||||
result.incl tfRequiresInit
|
||||
|
||||
proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
if n.len < 1:
|
||||
result = newConstraint(c, kind)
|
||||
else:
|
||||
if prevIsKind(prev, kind) and tfRefsAnonObj in prev.skipTypes({tyGenericBody}).flags:
|
||||
# the symbol already has an object type (likely resem), don't create a new type
|
||||
return skipGenericPrev(prev)
|
||||
let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr})
|
||||
let n = if n[0].kind == nkBracket: n[0] else: n
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
@@ -1090,20 +1136,22 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
addSonSkipIntLit(result, region, c.idgen)
|
||||
addSonSkipIntLit(result, t, c.idgen)
|
||||
if tfPartial in result.flags:
|
||||
if result.elementType.kind == tyObject: incl(result.elementType.flags, tfPartial)
|
||||
if result.elementType.kind == tyObject: incl(result.elementType, tfPartial)
|
||||
# if not isNilable: result.flags.incl tfNotNil
|
||||
case wrapperKind
|
||||
of tyOwned:
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
let t = newTypeS(tyOwned, c, result)
|
||||
t.flags.incl tfHasOwned
|
||||
t.incl tfHasOwned
|
||||
result = t
|
||||
of tySink:
|
||||
let t = newTypeS(tySink, c, result)
|
||||
result = t
|
||||
else: discard
|
||||
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
result.flags.incl tfHasAsgn
|
||||
if result.kind == tyRef and
|
||||
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
tfTriggersCompileTime notin result.flags:
|
||||
result.incl tfHasAsgn
|
||||
|
||||
proc findEnforcedStaticType(t: PType): PType =
|
||||
# This handles types such as `static[T] and Foo`,
|
||||
@@ -1160,10 +1208,10 @@ proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
|
||||
let owner = if typeClass.sym != nil: typeClass.sym
|
||||
else: getCurrOwner(c)
|
||||
var s = newSym(skType, finalTypId, c.idgen, owner, info)
|
||||
if sfExplain in owner.flags: s.flags.incl sfExplain
|
||||
if typId == nil: s.flags.incl(sfAnon)
|
||||
if sfExplain in owner.flags: s.incl sfExplain
|
||||
if typId == nil: s.incl(sfAnon)
|
||||
s.linkTo(typeClass)
|
||||
typeClass.flags.incl tfImplicitTypeParam
|
||||
typeClass.incl tfImplicitTypeParam
|
||||
s.position = genericParams.len
|
||||
genericParams.add newSymNode(s)
|
||||
result = typeClass
|
||||
@@ -1196,7 +1244,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName)
|
||||
result = addImplicitGeneric(c, newTypeS(tyStatic, c, base),
|
||||
paramTypId, info, genericParams, paramName)
|
||||
if result != nil: result.flags.incl({tfHasStatic, tfUnresolved})
|
||||
if result != nil: result.incl({tfHasStatic, tfUnresolved})
|
||||
|
||||
of tyTypeDesc:
|
||||
if tfUnresolved notin paramType.flags:
|
||||
@@ -1207,7 +1255,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
# XXX Why doesn't this check for tyTypeDesc instead?
|
||||
paramTypId = nil
|
||||
let t = newTypeS(tyTypeDesc, c, paramType.base)
|
||||
incl t.flags, tfCheckedForDestructor
|
||||
incl t, tfCheckedForDestructor
|
||||
result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName)
|
||||
else:
|
||||
result = nil
|
||||
@@ -1257,7 +1305,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
for i in 0..<paramType.len - 1:
|
||||
if paramType[i].kind == tyStatic:
|
||||
var staticCopy = paramType[i].exactReplica
|
||||
staticCopy.flags.incl tfInferrableStatic
|
||||
staticCopy.incl tfInferrableStatic
|
||||
result.rawAddSon staticCopy
|
||||
else:
|
||||
result.rawAddSon newTypeS(tyAnything, c)
|
||||
@@ -1289,12 +1337,14 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
paramType[i] = lifted
|
||||
result = paramType
|
||||
result.last.shouldHaveMeta
|
||||
|
||||
let liftBody = recurse(paramType.skipModifier, true)
|
||||
if liftBody != nil:
|
||||
result = liftBody
|
||||
result.flags.incl tfHasMeta
|
||||
#result.shouldHaveMeta
|
||||
if paramType.isConcept:
|
||||
return addImplicitGeneric(c, paramType, paramTypId, info, genericParams, paramName)
|
||||
else:
|
||||
let liftBody = recurse(paramType.skipModifier, true)
|
||||
if liftBody != nil:
|
||||
result = liftBody
|
||||
result.incl tfHasMeta
|
||||
#result.shouldHaveMeta
|
||||
|
||||
of tyGenericInvocation:
|
||||
result = nil
|
||||
@@ -1308,7 +1358,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
# this may happen for proc type appearing in a type section
|
||||
# before one of its param types
|
||||
return
|
||||
|
||||
if body.last.kind == tyUserTypeClass:
|
||||
let expanded = instGenericContainer(c, info, paramType,
|
||||
allowMetaTypes = true)
|
||||
@@ -1325,7 +1374,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
markUsed(c, paramType.sym.info, paramType.sym)
|
||||
onUse(paramType.sym.info, paramType.sym)
|
||||
if tfWildcard in paramType.flags:
|
||||
paramType.flags.excl tfWildcard
|
||||
paramType.excl tfWildcard
|
||||
paramType.sym.transitionGenericParamToType()
|
||||
|
||||
else: result = nil
|
||||
@@ -1448,7 +1497,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# surprising behavior. We must instead fix the expected type of
|
||||
# the proc to be the unbound typedesc type:
|
||||
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
|
||||
typ.flags.incl tfCheckedForDestructor
|
||||
typ.incl tfCheckedForDestructor
|
||||
|
||||
elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
@@ -1465,13 +1514,15 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if isType: localError(c.config, a.info, "':' expected")
|
||||
if kind in {skTemplate, skMacro}:
|
||||
typ = newTypeS(tyUntyped, c)
|
||||
elif isRecursiveStructuralType(typ):
|
||||
localError(c.config, a[^2].info, errIllegalRecursionInTypeX % typeToString(typ))
|
||||
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
||||
continue
|
||||
|
||||
for j in 0..<a.len-2:
|
||||
var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
|
||||
if arg.name.id == ord(wUnderscore):
|
||||
arg.flags.incl(sfGenSym)
|
||||
arg.incl(sfGenSym)
|
||||
elif containsOrIncl(check, arg.name.id):
|
||||
localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||
if a[j].kind == nkPragmaExpr:
|
||||
@@ -1501,7 +1552,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
addParamOrResult(c, arg, kind)
|
||||
styleCheckDef(c, a[j].info, arg)
|
||||
onDef(a[j].info, arg)
|
||||
a[j] = newSymNode(arg)
|
||||
if a[j].kind == nkPragmaExpr:
|
||||
a[j][0] = newSymNode(arg)
|
||||
else:
|
||||
a[j] = newSymNode(arg)
|
||||
|
||||
var r: PType = nil
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -1525,14 +1579,16 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if r != nil:
|
||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||
# compiler only checks for 'nil':
|
||||
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
|
||||
if isRecursiveStructuralType(r):
|
||||
localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(r))
|
||||
elif skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
|
||||
if kind notin {skMacro, skTemplate} and r.kind in {tyTyped, tyUntyped}:
|
||||
localError(c.config, n[0].info, "return type '" & typeToString(r) &
|
||||
"' is only valid for macros and templates")
|
||||
# 'auto' as a return type does not imply a generic:
|
||||
elif r.kind == tyAnything:
|
||||
r = copyType(r, c.idgen, r.owner)
|
||||
r.flags.incl tfRetType
|
||||
r.incl tfRetType
|
||||
elif r.kind == tyStatic:
|
||||
# type allowed should forbid this type
|
||||
discard
|
||||
@@ -1544,13 +1600,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
r = lifted
|
||||
#if r.kind != tyGenericParam:
|
||||
#echo "came here for ", typeToString(r)
|
||||
r.flags.incl tfRetType
|
||||
r.incl tfRetType
|
||||
r = skipIntLit(r, c.idgen)
|
||||
if kind == skIterator:
|
||||
# see tchainediterators
|
||||
# in cases like iterator foo(it: iterator): typeof(it)
|
||||
# we don't need to change the return type to iter[T]
|
||||
result.flags.incl tfIterator
|
||||
result.incl tfIterator
|
||||
# XXX Would be nice if we could get rid of this
|
||||
result[0] = r
|
||||
let oldFlags = result.flags
|
||||
@@ -1558,17 +1614,17 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
if oldFlags != result.flags:
|
||||
# XXX This rather hacky way keeps 'tflatmap' compiling:
|
||||
if tfHasMeta notin oldFlags:
|
||||
result.flags.excl tfHasMeta
|
||||
result.excl tfHasMeta
|
||||
result.n.typ() = r
|
||||
|
||||
if isCurrentlyGeneric():
|
||||
for n in genericParams:
|
||||
if {sfUsed, sfAnon} * n.sym.flags == {}:
|
||||
result.flags.incl tfUnresolved
|
||||
result.incl tfUnresolved
|
||||
|
||||
if tfWildcard in n.sym.typ.flags:
|
||||
n.sym.transitionGenericParamToType()
|
||||
n.sym.typ.flags.excl tfWildcard
|
||||
n.sym.typ.excl tfWildcard
|
||||
|
||||
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
@@ -1656,6 +1712,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
for i in 1..<n.len:
|
||||
var elem = semGenericParamInInvocation(c, n[i])
|
||||
addToResult(elem, true)
|
||||
c.forwardTypeUpdates.add (result, n)
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
# we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
@@ -1704,7 +1761,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif containsGenericInvocationWithForward(n[0]):
|
||||
c.skipTypes.add n #fixes 1500
|
||||
c.forwardTypeUpdates.add (result, n) #fixes 1500
|
||||
else:
|
||||
result = instGenericContainer(c, n.info, result,
|
||||
allowMetaTypes = false)
|
||||
@@ -1712,7 +1769,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
# special check for generic object with
|
||||
# generic/partial specialized parent
|
||||
let tx = result.skipTypes(abstractPtrs, 50)
|
||||
if tx.isNil or isTupleRecursive(tx):
|
||||
if tx.isNil or isRecursiveStructuralType(tx):
|
||||
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
|
||||
return errorType(c)
|
||||
if tx != result and tx.kind == tyObject:
|
||||
@@ -1720,7 +1777,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if not trySemObjectTypeForInheritedGenericInst(c, n, tx):
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
var position = 0
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
# it can be that we cached this generic instance. In this case, we don't have to
|
||||
# recompute the field positions:
|
||||
if tx.state != Sealed:
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
|
||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
if prev != nil and (prev.kind == tyGenericBody or
|
||||
@@ -1733,10 +1793,10 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
|
||||
proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
|
||||
if prev != nil:
|
||||
let result = newTypeS(tyAlias, c)
|
||||
result.rawAddSon typExpr.typ
|
||||
result.rawAddSon typ
|
||||
result.sym = prev.sym
|
||||
if prev.kind != tyGenericBody:
|
||||
assignType(prev, result)
|
||||
@@ -1790,7 +1850,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
# if n.len == 0: return newConstraint(c, tyTypeClass)
|
||||
if isNewStyleConcept(n):
|
||||
result = newOrPrevType(tyConcept, prev, c)
|
||||
result.flags.incl tfCheckedForDestructor
|
||||
result.incl tfCheckedForDestructor
|
||||
result.n = semConceptDeclaration(c, n)
|
||||
return result
|
||||
|
||||
@@ -1801,7 +1861,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
var owner = getCurrOwner(c)
|
||||
var candidateTypeSlot = newTypeS(tyAlias, c, c.errorType)
|
||||
result = newOrPrevType(tyUserTypeClass, prev, c, son = candidateTypeSlot)
|
||||
result.flags.incl tfCheckedForDestructor
|
||||
result.incl tfCheckedForDestructor
|
||||
result.n = n
|
||||
|
||||
if inherited.kind != nkEmpty:
|
||||
@@ -1823,8 +1883,8 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
# if modifier == tyRef:
|
||||
# dummyType.flags.incl tfNotNil
|
||||
if modifier == tyTypeDesc:
|
||||
dummyType.flags.incl tfConceptMatchedTypeSym
|
||||
dummyType.flags.incl tfCheckedForDestructor
|
||||
dummyType.incl tfConceptMatchedTypeSym
|
||||
dummyType.incl tfCheckedForDestructor
|
||||
else:
|
||||
dummyName = param
|
||||
dummyType = candidateTypeSlot
|
||||
@@ -1837,7 +1897,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
|
||||
dummyName.ident, c.idgen, owner, param.info)
|
||||
dummyParam.typ = dummyType
|
||||
incl dummyParam.flags, sfUsed
|
||||
incl dummyParam.flagsImpl, sfUsed
|
||||
addDecl(c, dummyParam)
|
||||
|
||||
result.n[3] = semConceptBody(c, n[3])
|
||||
@@ -1922,18 +1982,25 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
result = newOrPrevType(tyStatic, prev, c)
|
||||
var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
|
||||
result.rawAddSon(base)
|
||||
result.flags.incl tfHasStatic
|
||||
result.incl tfHasStatic
|
||||
|
||||
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})
|
||||
let ex = semExprWithType(c, n, {efInTypeof})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
result = ex.typ
|
||||
if result.kind == tyFromExpr:
|
||||
result.flags.incl tfNonConstExpr
|
||||
result.incl tfNonConstExpr
|
||||
elif result.kind == tyStatic:
|
||||
let base = result.skipTypes({tyStatic})
|
||||
if c.inGenericContext > 0 and base.containsGenericType:
|
||||
result = makeTypeFromExpr(c, copyTree(ex))
|
||||
result.incl tfNonConstExpr
|
||||
else:
|
||||
result = base
|
||||
fixupTypeOf(c, prev, result)
|
||||
|
||||
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
@@ -1946,12 +2013,19 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
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: {})
|
||||
let ex = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
|
||||
closeScope(c)
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
result = ex.typ
|
||||
if result.kind == tyFromExpr:
|
||||
result.flags.incl tfNonConstExpr
|
||||
result.incl tfNonConstExpr
|
||||
elif result.kind == tyStatic:
|
||||
let base = result.skipTypes({tyStatic})
|
||||
if c.inGenericContext > 0 and base.containsGenericType:
|
||||
result = makeTypeFromExpr(c, copyTree(ex))
|
||||
result.incl tfNonConstExpr
|
||||
else:
|
||||
result = base
|
||||
fixupTypeOf(c, prev, result)
|
||||
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkSym:
|
||||
@@ -1982,14 +2056,14 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
return errorSym(c, n)
|
||||
result = result.typ.sym.copySym(c.idgen)
|
||||
result.typ = exactReplica(result.typ)
|
||||
result.typ.flags.incl tfUnresolved
|
||||
result.typ.incl tfUnresolved
|
||||
|
||||
if result.kind == skGenericParam:
|
||||
if result.typ.kind == tyGenericParam and result.typ.len == 0 and
|
||||
tfWildcard in result.typ.flags:
|
||||
# collapse the wild-card param to a type
|
||||
result.transitionGenericParamToType()
|
||||
result.typ.flags.excl tfWildcard
|
||||
result.typ.excl tfWildcard
|
||||
return
|
||||
else:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
@@ -2031,7 +2105,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
# for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
|
||||
checkSonsLen(n, 1, c.config)
|
||||
result = semTypeOf(c, n[0], prev)
|
||||
if result.kind == tyTypeDesc: result.flags.incl tfExplicit
|
||||
if result.kind == tyTypeDesc: result.incl tfExplicit
|
||||
of nkPar:
|
||||
if n.len == 1: result = semTypeNode(c, n[0], prev)
|
||||
else:
|
||||
@@ -2051,7 +2125,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).kind in NilableTypes+GenericTypes:
|
||||
if tfNotNil in result.flags:
|
||||
result = freshType(c, result, prev)
|
||||
result.flags.excl(tfNotNil)
|
||||
result.excl(tfNotNil)
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated, "invalid type")
|
||||
elif n[0].kind notin nkIdentKinds:
|
||||
@@ -2114,7 +2188,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = makeTypeFromExpr(c, newTree(nkStmtListType, n.copyTree))
|
||||
of NilableTypes + {tyGenericInvocation, tyForward}:
|
||||
result = freshType(c, result, prev)
|
||||
result.flags.incl(tfNotNil)
|
||||
result.incl(tfNotNil)
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated, "invalid type")
|
||||
of 2:
|
||||
@@ -2160,11 +2234,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mSeq:
|
||||
result = semContainer(c, n, tySequence, "seq", prev)
|
||||
if optSeqDestructors in c.config.globalOptions:
|
||||
incl result.flags, tfHasAsgn
|
||||
incl result, tfHasAsgn
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mTypeDesc, mType, mTypeOf:
|
||||
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
result.flags.incl tfExplicit
|
||||
result.incl tfExplicit
|
||||
of mStatic:
|
||||
result = semStaticType(c, n[1], prev)
|
||||
of mExpr:
|
||||
@@ -2283,7 +2357,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
let child = newTypeS(tyProc, c)
|
||||
if n.kind == nkIteratorTy:
|
||||
child.flags.incl tfIterator
|
||||
child.incl tfIterator
|
||||
if n.len > 0 and n[1].kind != nkEmpty and n[1].len > 0:
|
||||
# typeclass with pragma
|
||||
let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
|
||||
@@ -2301,9 +2375,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
|
||||
if n.kind == nkIteratorTy and result.kind == tyProc:
|
||||
result.flags.incl(tfIterator)
|
||||
result.incl(tfIterator)
|
||||
if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
result.flags.incl tfHasAsgn
|
||||
result.incl tfHasAsgn
|
||||
of nkEnumTy: result = semEnum(c, n, prev)
|
||||
of nkType: result = n.typ
|
||||
of nkStmtListType: result = semStmtListType(c, n, prev)
|
||||
@@ -2333,7 +2407,7 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
||||
|
||||
proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
|
||||
setMagicType(conf, m, kind, size)
|
||||
incl m.typ.flags, tfCheckedForDestructor
|
||||
incl m.typ, tfCheckedForDestructor
|
||||
|
||||
proc processMagicType(c: PContext, m: PSym) =
|
||||
case m.magic
|
||||
@@ -2357,7 +2431,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
setMagicType(c.config, m, tyString, szUncomputedSize)
|
||||
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
|
||||
if optSeqDestructors in c.config.globalOptions:
|
||||
incl m.typ.flags, tfHasAsgn
|
||||
incl m.typ, tfHasAsgn
|
||||
of mCstring:
|
||||
setMagicIntegral(c.config, m, tyCstring, c.config.target.ptrSize)
|
||||
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
|
||||
@@ -2394,7 +2468,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of mSeq:
|
||||
setMagicType(c.config, m, tySequence, szUncomputedSize)
|
||||
if optSeqDestructors in c.config.globalOptions:
|
||||
incl m.typ.flags, tfHasAsgn
|
||||
incl m.typ, tfHasAsgn
|
||||
if defined(nimsuggest) or c.config.cmd == cmdCheck: # bug #18985
|
||||
discard
|
||||
else:
|
||||
@@ -2407,8 +2481,8 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
setMagicIntegral(c.config, m, tyIterable, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mPNimrodNode:
|
||||
incl m.typ.flags, tfTriggersCompileTime
|
||||
incl m.typ.flags, tfCheckedForDestructor
|
||||
incl m.typ, tfTriggersCompileTime
|
||||
incl m.typ, tfCheckedForDestructor
|
||||
of mException: discard
|
||||
of mBuiltinType:
|
||||
case m.name.s
|
||||
@@ -2416,7 +2490,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of "sink": setMagicType(c.config, m, tySink, szUncomputedSize)
|
||||
of "owned":
|
||||
setMagicType(c.config, m, tyOwned, c.config.target.ptrSize)
|
||||
incl m.typ.flags, tfHasOwned
|
||||
incl m.typ, tfHasOwned
|
||||
else: localError(c.config, m.info, errTypeExpected)
|
||||
else: localError(c.config, m.info, errTypeExpected)
|
||||
|
||||
@@ -2449,7 +2523,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
if typ.kind == tyTypeDesc:
|
||||
if typ.elementType.kind == tyNone:
|
||||
typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
|
||||
incl typ.flags, tfCheckedForDestructor
|
||||
incl typ, tfCheckedForDestructor
|
||||
else:
|
||||
typ = semGenericConstraints(c, typ)
|
||||
|
||||
@@ -2467,9 +2541,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
|
||||
if typ == nil:
|
||||
typ = newTypeS(tyGenericParam, c)
|
||||
if father == nil: typ.flags.incl tfWildcard
|
||||
if father == nil: typ.incl tfWildcard
|
||||
|
||||
typ.flags.incl tfGenericTypeParam
|
||||
typ.incl tfGenericTypeParam
|
||||
|
||||
for j in 0..<a.len-2:
|
||||
var finalType: PType
|
||||
@@ -2491,7 +2565,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
localError(c.config, paramName.info, errInOutFlagNotExtern % $paramName[0])
|
||||
covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
|
||||
else: tfCovariant
|
||||
if father != nil: father.flags.incl tfCovariant
|
||||
if father != nil: father.incl tfCovariant
|
||||
paramName = paramName[1]
|
||||
|
||||
var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
|
||||
@@ -2499,7 +2573,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
else:
|
||||
newSymG(skType, paramName, c).linkTo(finalType)
|
||||
|
||||
if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
|
||||
if covarianceFlag != tfUnresolved: s.typ.incl(covarianceFlag)
|
||||
if def.kind != nkEmpty: s.ast = def
|
||||
s.position = result.len
|
||||
result.addSym(s)
|
||||
|
||||
@@ -28,7 +28,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
|
||||
if t.kind in tyTypeClasses: discard
|
||||
elif t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
|
||||
localError(conf, info, "type 'var var' is not allowed")
|
||||
elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t):
|
||||
elif computeSize(conf, t) == szIllegalRecursion or isRecursiveStructuralType(t):
|
||||
localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")
|
||||
|
||||
proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
|
||||
@@ -68,8 +68,8 @@ type
|
||||
TReplTypeVars* = object
|
||||
c*: PContext
|
||||
typeMap*: LayeredIdTable # map PType to PType
|
||||
symMap*: SymMapping # map PSym to PSym
|
||||
localCache*: TypeMapping # local cache for remembering already replaced
|
||||
symMap*: SymMapping # map PSym to PSym
|
||||
localCache*: TypeMapping # local cache for remembering already replaced
|
||||
# types during instantiation of meta types
|
||||
# (they are not stored in the global cache)
|
||||
info*: TLineInfo
|
||||
@@ -263,7 +263,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
|
||||
if n.typ != nil:
|
||||
if n.typ.kind == tyFromExpr:
|
||||
# type of node should not be evaluated as a static value
|
||||
n.typ.flags.incl tfNonConstExpr
|
||||
n.typ.incl tfNonConstExpr
|
||||
result.typ() = replaceTypeVarsT(cl, n.typ)
|
||||
checkMetaInvariants(cl, result.typ)
|
||||
case n.kind
|
||||
@@ -279,8 +279,10 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
|
||||
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
|
||||
var n = result.sym.ast
|
||||
cl.c.fitDefaultNode(cl.c, n, result.sym.typ)
|
||||
result.sym.ast = n
|
||||
result.sym.typ = n.typ.skipIntLit(cl.c.idgen)
|
||||
# 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
|
||||
@@ -361,7 +363,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
|
||||
|
||||
]#
|
||||
result = copySym(s, cl.c.idgen)
|
||||
incl(result.flags, sfFromGeneric)
|
||||
incl(result.flagsImpl, sfFromGeneric)
|
||||
#idTablePut(cl.symMap, s, result)
|
||||
setOwner(result, s.owner)
|
||||
result.typ = t
|
||||
@@ -394,12 +396,12 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||
#cl.typeMap.topLayer.idTablePut(result, t)
|
||||
|
||||
if cl.allowMetaTypes: return
|
||||
result.flags.incl tfFromGeneric
|
||||
result.incl tfFromGeneric
|
||||
if not (t.kind in tyMetaTypes or
|
||||
(t.kind == tyStatic and t.n == nil)):
|
||||
result.flags.excl tfInstClearedFlags
|
||||
result.excl tfInstClearedFlags
|
||||
else:
|
||||
result.flags.excl tfHasAsgn
|
||||
result.excl tfHasAsgn
|
||||
when false:
|
||||
if newDestructors:
|
||||
result.assignment = nil
|
||||
@@ -524,13 +526,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
let mm = skipTypes(bbody, abstractPtrs)
|
||||
if tfFromGeneric notin mm.flags:
|
||||
# bug #5479, prevent endless recursions here:
|
||||
incl mm.flags, tfFromGeneric
|
||||
incl mm.flagsImpl, tfFromGeneric
|
||||
for col, meth in methodsForGeneric(cl.c.graph, mm):
|
||||
# we instantiate the known methods belonging to that type, this causes
|
||||
# them to be registered and that's enough, so we 'discard' the result.
|
||||
discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
|
||||
attachedAsgn, col)
|
||||
excl mm.flags, tfFromGeneric
|
||||
excl mm.flagsImpl, tfFromGeneric
|
||||
|
||||
proc eraseVoidParams*(t: PType) =
|
||||
# transform '(): void' into '()' because old parts of the compiler really
|
||||
@@ -608,10 +610,13 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
result = t
|
||||
if t == nil: return
|
||||
|
||||
var et = t
|
||||
if t.isConcept:
|
||||
et = t.reduceToBase
|
||||
const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything}
|
||||
if t.kind in lookupMetas or
|
||||
(t.kind == tyAnything and tfRetType notin t.flags):
|
||||
let lookup = cl.typeMap.lookup(t)
|
||||
if et.kind in lookupMetas or
|
||||
(et.kind == tyAnything and tfRetType notin et.flags):
|
||||
let lookup = cl.typeMap.lookup(et)
|
||||
if lookup != nil: return lookup
|
||||
|
||||
case t.kind
|
||||
@@ -679,7 +684,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
|
||||
of tyUserTypeClass:
|
||||
result = t
|
||||
|
||||
|
||||
of tyStatic:
|
||||
if cl.c.matchedConcept != nil:
|
||||
# allow concepts to not instantiate statics for now
|
||||
@@ -751,7 +756,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
of tyObject, tyTuple:
|
||||
propagateFieldFlags(result, result.n)
|
||||
if result.kind == tyObject and cl.c.computeRequiresInit(cl.c, result):
|
||||
result.flags.incl tfRequiresInit
|
||||
result.incl tfRequiresInit
|
||||
|
||||
of tyProc:
|
||||
eraseVoidParams(result)
|
||||
|
||||
@@ -143,15 +143,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
|
||||
c.hashSym(t.sym)
|
||||
of tyObject, tyEnum:
|
||||
if t.typeInst != nil:
|
||||
if t.typeInstImpl != nil:
|
||||
# prevent against infinite recursions here, see bug #8883:
|
||||
let inst = t.typeInst
|
||||
t.typeInst = nil
|
||||
let inst = t.typeInstImpl
|
||||
t.typeInstImpl = nil # IC: spurious writes are ok since we set it back immediately
|
||||
assert inst.kind == tyGenericInst
|
||||
c.hashType inst.genericHead, flags, conf
|
||||
for _, a in inst.genericInstParams:
|
||||
c.hashType a, flags, conf
|
||||
t.typeInst = inst
|
||||
t.typeInstImpl = inst
|
||||
return
|
||||
c &= char(t.kind)
|
||||
# Every cyclic type in Nim need to be constructed via some 't.sym', so this
|
||||
@@ -180,9 +180,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
# Hack to prevent endless recursion
|
||||
# xxx instead, use a hash table to indicate we've already visited a type, which
|
||||
# would also be more efficient.
|
||||
symWithFlags.flags.excl {sfAnon, sfGenSym}
|
||||
symWithFlags.flagsImpl.excl {sfAnon, sfGenSym}
|
||||
hashTree(c, t.n, flags + {CoHashTypeInsideNode}, conf)
|
||||
symWithFlags.flags = oldFlags
|
||||
symWithFlags.flagsImpl = oldFlags
|
||||
else:
|
||||
# The object has no fields: we _must_ add something here in order to
|
||||
# make the hash different from the one we produce by hashing only the
|
||||
|
||||
@@ -20,6 +20,7 @@ import std/[intsets, strutils, tables]
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
|
||||
type
|
||||
MismatchKind* = enum
|
||||
kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
|
||||
@@ -94,6 +95,7 @@ type
|
||||
trNoCovariance
|
||||
trBindGenericParam # bind tyGenericParam even with trDontBind
|
||||
trIsOutParam
|
||||
trCheckGeneric
|
||||
|
||||
TTypeRelFlags* = set[TTypeRelFlag]
|
||||
|
||||
@@ -102,7 +104,7 @@ const
|
||||
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
|
||||
maxInheritancePenalty = high(int) div 2
|
||||
|
||||
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
|
||||
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true; isGenericInstance = false)
|
||||
proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
|
||||
|
||||
proc initCandidateAux(ctx: PContext,
|
||||
@@ -297,9 +299,9 @@ proc checkGeneric(a, b: TCandidate): int =
|
||||
var winner = 0
|
||||
for aai, bbi in underspecifiedPairs(aa, bb, 1):
|
||||
var ma = newCandidate(c, bbi)
|
||||
let tra = typeRel(ma, bbi, aai, {trDontBind})
|
||||
let tra = typeRel(ma, bbi, aai, {trDontBind, trCheckGeneric})
|
||||
var mb = newCandidate(c, aai)
|
||||
let trb = typeRel(mb, aai, bbi, {trDontBind})
|
||||
let trb = typeRel(mb, aai, bbi, {trDontBind, trCheckGeneric})
|
||||
if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
|
||||
if winner == -1: return 0
|
||||
winner = 1
|
||||
@@ -363,6 +365,8 @@ proc sumGeneric(t: PType): int =
|
||||
result += sumGeneric(a)
|
||||
break
|
||||
else:
|
||||
if t.isConcept:
|
||||
result += t.reduceToBase.conceptBody.len
|
||||
break
|
||||
|
||||
proc complexDisambiguation(a, b: PType): int =
|
||||
@@ -893,7 +897,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||
param.typ = typ.exactReplica
|
||||
#copyType(typ, c.idgen, typ.owner)
|
||||
if typ.n == nil:
|
||||
param.typ.flags.incl tfInferrableStatic
|
||||
param.typ.incl tfInferrableStatic
|
||||
else:
|
||||
param.ast = typ.n
|
||||
of tyFromExpr:
|
||||
@@ -926,7 +930,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
|
||||
diagnostics = @[]
|
||||
flags = {efExplain}
|
||||
m.c.config.writelnHook = proc (s: string) =
|
||||
if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
|
||||
{.gcsafe.}:
|
||||
if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
|
||||
let msg = s.replace("Error:", errorPrefix)
|
||||
if oldWriteHook != nil: oldWriteHook msg
|
||||
diagnostics.add msg
|
||||
@@ -1138,6 +1143,24 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
|
||||
else:
|
||||
return false
|
||||
|
||||
proc enterConceptMatch(c: var TCandidate; f,a: PType, flags: TTypeRelFlags): TTypeRelation =
|
||||
var
|
||||
conceptFlags: set[MatchFlags] = {}
|
||||
container: PType = nil
|
||||
concpt = f
|
||||
if concpt.kind != tyConcept:
|
||||
container = concpt
|
||||
concpt = container.reduceToBase
|
||||
if trDontBind in flags:
|
||||
conceptFlags.incl mfDontBind
|
||||
if trCheckGeneric in flags:
|
||||
conceptFlags.incl mfCheckGeneric
|
||||
let mres = concepts.conceptMatch(c.c, concpt, a, c.bindings, container, flags = conceptFlags)
|
||||
if mres:
|
||||
isGeneric
|
||||
else:
|
||||
isNone
|
||||
|
||||
when false:
|
||||
proc maxNumericType(prev, candidate: PType): PType =
|
||||
let c = candidate.skipTypes({tyRange})
|
||||
@@ -1212,9 +1235,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
var candidate = f
|
||||
|
||||
case f.kind
|
||||
let fType = f.skipTypes({tySink})
|
||||
case fType.kind
|
||||
of tyGenericParam:
|
||||
var prev = lookup(c.bindings, f)
|
||||
var prev = lookup(c.bindings, fType)
|
||||
if prev != nil: candidate = prev
|
||||
of tyFromExpr:
|
||||
let computedType = tryResolvingStaticExpr(c, f.n).typ
|
||||
@@ -1424,7 +1448,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
return isNone
|
||||
|
||||
if fRange.rangeHasUnresolvedStatic:
|
||||
if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
|
||||
if (aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange) or
|
||||
(aRange.kind == tyRange and aRange.rangeHasUnresolvedStatic):
|
||||
return
|
||||
return inferStaticsInRange(c, fRange, a)
|
||||
elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
|
||||
@@ -1517,12 +1542,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
reduceToBase(a)
|
||||
if effectiveArgType.kind == tyObject:
|
||||
if sameObjectTypes(f, effectiveArgType):
|
||||
c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
|
||||
if tfFinal notin f.flags:
|
||||
inc c.inheritancePenalty, ord(c.inheritancePenalty < 0)
|
||||
result = isEqual
|
||||
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
|
||||
elif trIsOutParam notin flags:
|
||||
c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
|
||||
if c.inheritancePenalty > 0:
|
||||
let depth = isObjectSubtype(c, effectiveArgType, f, nil)
|
||||
if depth > 0:
|
||||
inc c.inheritancePenalty, depth + ord(c.inheritancePenalty < 0)
|
||||
result = isSubtype
|
||||
of tyDistinct:
|
||||
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
|
||||
@@ -1542,8 +1569,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# set['a'..'z'] and set[char] have different representations
|
||||
result = isNone
|
||||
else:
|
||||
# but we can convert individual elements of the constructor
|
||||
result = isConvertible
|
||||
if result >= isConvertible:
|
||||
# but we can convert individual elements of the constructor
|
||||
result = isConvertible
|
||||
else:
|
||||
result = isNone
|
||||
of tyPtr, tyRef:
|
||||
a = reduceToBase(a)
|
||||
if a.kind == f.kind:
|
||||
@@ -1643,7 +1673,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a
|
||||
let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f
|
||||
|
||||
if a.kind == tyGenericInst:
|
||||
if f.isConcept:
|
||||
result = enterConceptMatch(c, rootf, roota, flags)
|
||||
elif a.kind == tyGenericInst:
|
||||
if roota.base == rootf.base:
|
||||
let nextFlags = flags + {trNoCovariance}
|
||||
var hasCovariance = false
|
||||
@@ -1692,7 +1724,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if aAsObject.kind == tyObject and trIsOutParam notin flags:
|
||||
let baseType = aAsObject.base
|
||||
if baseType != nil:
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
if tfFinal notin aAsObject.flags:
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
let ret = typeRel(c, f, baseType, flags)
|
||||
return if ret in {isEqual,isGeneric}: isSubtype else: ret
|
||||
|
||||
@@ -1713,7 +1746,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
var x = a.skipGenericAlias
|
||||
if x.kind == tyGenericParam and x.len > 0:
|
||||
x = x.last
|
||||
let concpt = f[0].skipTypes({tyGenericBody})
|
||||
let concpt = f.reduceToBase
|
||||
var preventHack = concpt.kind == tyConcept
|
||||
if x.kind == tyOwned and f[0].kind != tyOwned:
|
||||
preventHack = true
|
||||
@@ -1733,6 +1766,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let tr = typeRel(c, f[i], x[i], flags)
|
||||
if tr <= isSubtype: return
|
||||
result = isGeneric
|
||||
let impl = last(f[0])
|
||||
if impl.kind == tyObject and tfFinal notin impl.flags:
|
||||
# match non-invocation case
|
||||
inc c.inheritancePenalty, 0 + int(c.inheritancePenalty < 0)
|
||||
elif x.kind == tyGenericInst and f[0] == x[0] and
|
||||
x.len - 1 == f.len:
|
||||
for i in 1..<f.len:
|
||||
@@ -1742,9 +1779,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# Workaround for regression #4589
|
||||
if f[i].kind != tyTypeDesc: return
|
||||
result = isGeneric
|
||||
elif x.kind == tyGenericInst and concpt.kind == tyConcept:
|
||||
result = if concepts.conceptMatch(c.c, concpt, x, c.bindings, f): isGeneric
|
||||
else: isNone
|
||||
elif concpt.kind == tyConcept:
|
||||
result = enterConceptMatch(c, f, x, flags)
|
||||
else:
|
||||
let genericBody = f[0]
|
||||
var askip = skippedNone
|
||||
@@ -1752,7 +1788,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let aobj = x.skipToObject(askip)
|
||||
let fobj = genericBody.last.skipToObject(fskip)
|
||||
result = typeRel(c, genericBody, x, flags)
|
||||
if result != isNone:
|
||||
if result != isNone and concpt.kind != tyConcept:
|
||||
# see tests/generics/tgeneric3.nim for an example that triggers this
|
||||
# piece of code:
|
||||
#
|
||||
@@ -1789,7 +1825,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
depth = -1
|
||||
|
||||
if depth >= 0:
|
||||
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
|
||||
if aobj.kind == tyObject and tfFinal notin aobj.flags:
|
||||
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
|
||||
# bug #4863: We still need to bind generic alias crap, so
|
||||
# we cannot return immediately:
|
||||
result = if depth == 0: isGeneric else: isSubtype
|
||||
@@ -1815,9 +1852,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if c.inheritancePenalty > -1:
|
||||
minInheritance = min(minInheritance, c.inheritancePenalty)
|
||||
result = x
|
||||
c.inheritancePenalty = oldInheritancePenalty
|
||||
if result >= isIntConv:
|
||||
if minInheritance < maxInheritancePenalty:
|
||||
c.inheritancePenalty = oldInheritancePenalty + minInheritance
|
||||
inc c.inheritancePenalty, minInheritance + ord(c.inheritancePenalty < 0)
|
||||
if result > isGeneric: result = isGeneric
|
||||
bindingRet result
|
||||
else:
|
||||
@@ -1879,11 +1917,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
else:
|
||||
result = isNone
|
||||
of tyConcept:
|
||||
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
|
||||
result = enterConceptMatch(c, f, a, flags)
|
||||
of tyCompositeTypeClass:
|
||||
considerPreviousT:
|
||||
let roota = a.skipGenericAlias
|
||||
@@ -1956,7 +1990,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
var concrete = a
|
||||
if tfWildcard in a.flags:
|
||||
a.sym.transitionGenericParamToType()
|
||||
a.flags.excl tfWildcard
|
||||
a.excl tfWildcard
|
||||
elif doBind:
|
||||
# careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph.
|
||||
# typRel having two different modes (binding and non-binding) can make things harder to
|
||||
@@ -2155,6 +2189,8 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
|
||||
# keep varness
|
||||
if arg.typ != nil and arg.typ.kind == tyVar:
|
||||
result.typ() = toVar(result.typ, tyVar, c.idgen)
|
||||
# copy the tfVarIsPtr flag
|
||||
result.typ.flags = arg.typ.flags
|
||||
else:
|
||||
result.typ() = result.typ.skipTypes({tyVar})
|
||||
|
||||
@@ -2264,7 +2300,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
# for generic type converters we need to check 'src <- a' before
|
||||
# 'f <- dest' in order to not break the unification:
|
||||
# see tests/tgenericconverter:
|
||||
let srca = typeRel(m, src, a)
|
||||
var convMatch = newCandidate(c, src)
|
||||
let srca = typeRel(convMatch, src, a)
|
||||
if srca notin {isEqual, isGeneric, isSubtype}: continue
|
||||
|
||||
# What's done below matches the logic in ``matchesAux``
|
||||
@@ -2276,11 +2313,11 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
|
||||
let destIsGeneric = containsGenericType(dest)
|
||||
if destIsGeneric:
|
||||
dest = generateTypeInstance(c, m.bindings, arg, dest)
|
||||
dest = generateTypeInstance(c, convMatch.bindings, arg, dest)
|
||||
let fdest = typeRel(m, f, dest)
|
||||
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
|
||||
# can't fully mark used yet, may not be used in final call
|
||||
incl(c.converters[i].flags, sfUsed)
|
||||
incl(c.converters[i].flagsImpl, sfUsed)
|
||||
markOwnerModuleAsUsed(c, c.converters[i])
|
||||
var s = newSymNode(c.converters[i])
|
||||
s.typ() = c.converters[i].typ
|
||||
@@ -2292,7 +2329,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
# it is correct
|
||||
var param: PNode = nil
|
||||
if srca == isSubtype:
|
||||
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
|
||||
# convMatch used here to use its bindings to instantiate subtype:
|
||||
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), convMatch, c)
|
||||
elif src.kind in {tyVar}:
|
||||
# Analyse the converter return type.
|
||||
param = newNodeIT(nkHiddenAddr, arg.info, s.typ.firstParamType)
|
||||
@@ -2302,7 +2340,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
result.add param
|
||||
|
||||
if dest.kind in {tyVar, tyLent}:
|
||||
dest.flags.incl tfVarIsPtr
|
||||
dest.incl tfVarIsPtr
|
||||
result = newDeref(result)
|
||||
|
||||
inc(m.convMatches)
|
||||
@@ -2398,13 +2436,12 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
if m.callee.kind == tyGenericBody:
|
||||
if f.kind == tyStatic and typeRel(m, f.base, a) != isNone:
|
||||
result = makeStaticExpr(m.c, arg)
|
||||
result.typ.flags.incl tfUnresolved
|
||||
result.typ.incl tfUnresolved
|
||||
result.typ.n = arg
|
||||
return
|
||||
|
||||
let oldInheritancePenalty = m.inheritancePenalty
|
||||
var r = typeRel(m, f, a)
|
||||
|
||||
# This special typing rule for macros and templates is not documented
|
||||
# anywhere and breaks symmetry. It's hard to get rid of though, my
|
||||
# custom seqs example fails to compile without this:
|
||||
@@ -2959,7 +2996,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
|
||||
#assert(container == nil)
|
||||
if container.isNil:
|
||||
container = newNodeIT(nkBracket, n[a].info, arrayConstr(c, arg))
|
||||
container.typ.flags.incl tfVarargs
|
||||
container.typ.incl tfVarargs
|
||||
else:
|
||||
incrIndexType(container.typ)
|
||||
container.add arg
|
||||
|
||||
@@ -45,7 +45,8 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod
|
||||
#echo config $ arg.info, " turned into a sink parameter ", arg.sym.name.s
|
||||
elif sfWasForwarded notin arg.sym.flags:
|
||||
# we only report every potential 'sink' parameter only once:
|
||||
incl arg.sym.flags, sfWasForwarded
|
||||
ensureMutable arg.sym
|
||||
incl arg.sym.flagsImpl, sfWasForwarded
|
||||
message(config, arg.info, hintPerformance,
|
||||
"could not turn '$1' to a sink parameter" % [arg.sym.name.s])
|
||||
#echo config $ arg.info, " candidate for a sink parameter here"
|
||||
|
||||
@@ -58,7 +58,7 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator;
|
||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info,
|
||||
owner.options)
|
||||
result.typ = typ
|
||||
incl(result.flags, sfFromGeneric)
|
||||
incl(result.flagsImpl, sfFromGeneric)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
vpart[0] = newSymNode(result)
|
||||
@@ -358,7 +358,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
|
||||
threadParam = newSym(skParam, getIdent(g.cache, "thread"), idgen, wrapperProc, n.info, g.config.options)
|
||||
argsParam = newSym(skParam, getIdent(g.cache, "args"), idgen, wrapperProc, n.info, g.config.options)
|
||||
|
||||
wrapperProc.flags.incl sfInjectDestructors
|
||||
wrapperProc.incl sfInjectDestructors
|
||||
block:
|
||||
let ptrType = getSysType(g, n.info, tyPointer)
|
||||
threadParam.typ = ptrType
|
||||
@@ -366,13 +366,13 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
|
||||
argsParam.position = 1
|
||||
|
||||
var objType = createObj(g, idgen, owner, n.info)
|
||||
incl(objType.flags, tfFinal)
|
||||
incl(objType, tfFinal)
|
||||
let castExpr = createCastExpr(argsParam, objType, idgen)
|
||||
|
||||
var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), idgen, owner, n.info, g.config.options)
|
||||
block:
|
||||
scratchObj.typ = objType
|
||||
incl(scratchObj.flags, sfFromGeneric)
|
||||
incl(scratchObj.flagsImpl, sfFromGeneric)
|
||||
var varSectionB = newNodeI(nkVarSection, n.info)
|
||||
varSectionB.addVar(scratchObj.newSymNode)
|
||||
result.add varSectionB
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
import prefixmatches, suggestsymdb
|
||||
from wordrecg import wDeprecated, wError, wAddr, wYield
|
||||
|
||||
import std/[algorithm, sets, parseutils, tables]
|
||||
import std/[algorithm, sets, parseutils, os]
|
||||
|
||||
when defined(nimsuggest):
|
||||
import pathutils # importer
|
||||
@@ -43,6 +43,12 @@ when defined(nimsuggest):
|
||||
const
|
||||
sep = '\t'
|
||||
|
||||
type
|
||||
ImportContext = object
|
||||
isMultiImport: bool # True if we're in a [...] context
|
||||
baseDir: string # e.g., "folder/" in "import folder/[..."
|
||||
partialModule: string # The actual module name being typed
|
||||
|
||||
#template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
|
||||
|
||||
template origModuleName(m: PSym): string = m.name.s
|
||||
@@ -584,7 +590,7 @@ when defined(nimsuggest):
|
||||
let infoAsInt = info.infoToInt
|
||||
for infoB in s.allUsages:
|
||||
if infoB.infoToInt == infoAsInt: return
|
||||
s.allUsages.add(info)
|
||||
s.allUsagesImpl.add(info)
|
||||
|
||||
proc findUsages(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
if g.config.suggestVersion == 1:
|
||||
@@ -618,41 +624,43 @@ proc ensureIdx[T](x: var T, y: int) =
|
||||
proc ensureSeq[T](x: var seq[T]) =
|
||||
if x == nil: newSeq(x, 0)
|
||||
|
||||
proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
|
||||
proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true; isGenericInstance=false) {.inline.} =
|
||||
## misnamed: should be 'symDeclared'
|
||||
let conf = g.config
|
||||
when defined(nimsuggest):
|
||||
g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions
|
||||
if optIdeExceptionInlayHints in conf.globalOptions or not isGenericInstance:
|
||||
g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl, isGenericInstance: isGenericInstance), optIdeExceptionInlayHints in g.config.globalOptions
|
||||
|
||||
if conf.suggestVersion == 0:
|
||||
if s.allUsages.len == 0:
|
||||
s.allUsages = @[info]
|
||||
else:
|
||||
s.addNoDup(info)
|
||||
if not isGenericInstance:
|
||||
if conf.suggestVersion == 0:
|
||||
if s.allUsages.len == 0:
|
||||
s.allUsages = @[info]
|
||||
else:
|
||||
s.addNoDup(info)
|
||||
|
||||
if conf.ideCmd == ideUse:
|
||||
findUsages(g, info, s, usageSym)
|
||||
elif conf.ideCmd == ideDef:
|
||||
findDefinition(g, info, s, usageSym)
|
||||
elif conf.ideCmd == ideDus and s != nil:
|
||||
if isTracked(info, conf.m.trackPos, s.name.s.len):
|
||||
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
|
||||
findUsages(g, info, s, usageSym)
|
||||
elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
|
||||
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
|
||||
elif conf.ideCmd == ideOutline and isDecl:
|
||||
# if a module is included then the info we have is inside the include and
|
||||
# we need to walk up the owners until we find the outer most module,
|
||||
# which will be the last skModule prior to an skPackage.
|
||||
var
|
||||
parentFileIndex = info.fileIndex # assume we're in the correct module
|
||||
parentModule = s.owner
|
||||
while parentModule != nil and parentModule.kind == skModule:
|
||||
parentFileIndex = parentModule.info.fileIndex
|
||||
parentModule = parentModule.owner
|
||||
if conf.ideCmd == ideUse:
|
||||
findUsages(g, info, s, usageSym)
|
||||
elif conf.ideCmd == ideDef:
|
||||
findDefinition(g, info, s, usageSym)
|
||||
elif conf.ideCmd == ideDus and s != nil:
|
||||
if isTracked(info, conf.m.trackPos, s.name.s.len):
|
||||
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
|
||||
findUsages(g, info, s, usageSym)
|
||||
elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
|
||||
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
|
||||
elif conf.ideCmd == ideOutline and isDecl:
|
||||
# if a module is included then the info we have is inside the include and
|
||||
# we need to walk up the owners until we find the outer most module,
|
||||
# which will be the last skModule prior to an skPackage.
|
||||
var
|
||||
parentFileIndex = info.fileIndex # assume we're in the correct module
|
||||
parentModule = s.owner
|
||||
while parentModule != nil and parentModule.kind == skModule:
|
||||
parentFileIndex = parentModule.info.fileIndex
|
||||
parentModule = parentModule.owner
|
||||
|
||||
if parentFileIndex == conf.m.trackPos.fileIndex:
|
||||
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
|
||||
if parentFileIndex == conf.m.trackPos.fileIndex:
|
||||
suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
|
||||
|
||||
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
var pragmaNode: PNode
|
||||
@@ -696,26 +704,28 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
|
||||
else:
|
||||
inc i
|
||||
|
||||
proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) =
|
||||
let conf = c.config
|
||||
incl(s.flags, sfUsed)
|
||||
if s.kind == skEnumField and s.owner != nil:
|
||||
incl(s.owner.flags, sfUsed)
|
||||
if sfDeprecated in s.owner.flags:
|
||||
warnAboutDeprecated(conf, info, s)
|
||||
if {sfDeprecated, sfError} * s.flags != {}:
|
||||
if sfDeprecated in s.flags:
|
||||
if not (c.lastTLineInfo.line == info.line and
|
||||
c.lastTLineInfo.col == info.col):
|
||||
proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true; isGenericInstance = false) =
|
||||
if not isGenericInstance:
|
||||
let conf = c.config
|
||||
incl(s.flagsImpl, sfUsed)
|
||||
if s.kind == skEnumField and s.owner != nil:
|
||||
incl(s.owner.flagsImpl, sfUsed)
|
||||
if sfDeprecated in s.owner.flags:
|
||||
warnAboutDeprecated(conf, info, s)
|
||||
c.lastTLineInfo = info
|
||||
if {sfDeprecated, sfError} * s.flags != {}:
|
||||
if sfDeprecated in s.flags:
|
||||
if not (c.lastTLineInfo.line == info.line and
|
||||
c.lastTLineInfo.col == info.col):
|
||||
warnAboutDeprecated(conf, info, s)
|
||||
c.lastTLineInfo = info
|
||||
|
||||
if sfError in s.flags: userError(conf, info, s)
|
||||
if sfError in s.flags: userError(conf, info, s)
|
||||
when defined(nimsuggest):
|
||||
suggestSym(c.graph, info, s, c.graph.usageSym, false)
|
||||
if checkStyle:
|
||||
styleCheckUse(c, info, s)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
suggestSym(c.graph, info, s, c.graph.usageSym, isDecl = false, isGenericInstance = isGenericInstance)
|
||||
if not isGenericInstance:
|
||||
if checkStyle:
|
||||
styleCheckUse(c, info, s)
|
||||
markOwnerModuleAsUsed(c, s)
|
||||
|
||||
proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||
# use only for idetools support!
|
||||
@@ -746,6 +756,123 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
|
||||
let prefix = if c.config.m.trackPosAttached: nil else: n
|
||||
suggestEverything(c, n, prefix, outputs)
|
||||
|
||||
proc extractImportContextFromAst(n: PNode, cursorCol: int): ImportContext =
|
||||
result = ImportContext()
|
||||
if n.kind != nkImportStmt: return
|
||||
for child in n:
|
||||
case child.kind
|
||||
of nkIdent:
|
||||
# Single import, e.g. import foo
|
||||
if child.info.col <= cursorCol:
|
||||
result.baseDir = ""
|
||||
result.partialModule = child.ident.s
|
||||
result.isMultiImport = false
|
||||
of nkInfix:
|
||||
# Directory or multi-import, e.g. import std/[os, strutils]
|
||||
if child.len == 3 and child[0].kind == nkIdent and child[0].ident.s == "/":
|
||||
let dir = child[1].ident.s
|
||||
if child[2].kind == nkBracket:
|
||||
result.baseDir = dir
|
||||
result.isMultiImport = true
|
||||
for modNode in child[2]:
|
||||
if modNode.kind == nkIdent and modNode.info.col <= cursorCol:
|
||||
result.partialModule = modNode.ident.s
|
||||
elif child[2].kind == nkIdent:
|
||||
if child[2].info.col <= cursorCol:
|
||||
result.baseDir = dir
|
||||
result.partialModule = child[2].ident.s
|
||||
result.isMultiImport = false
|
||||
else:
|
||||
discard
|
||||
|
||||
proc findModuleFile(c: PContext, partialPath: string): seq[string] =
|
||||
result = @[]
|
||||
let currentModuleDir = parentDir(toFullPath(c.config, FileIndex(c.module.position)))
|
||||
|
||||
proc tryAddModule(path, baseName: string) =
|
||||
if fileExists(path & ".nim"):
|
||||
result.add(baseName)
|
||||
|
||||
proc addModulesFromDir(dir, file: string; result: var seq[string]) =
|
||||
if dirExists(dir):
|
||||
for kind, path in walkDir(dir):
|
||||
if kind in {pcFile, pcDir}:
|
||||
let (_, name, ext) = splitFile(path)
|
||||
if kind == pcFile:
|
||||
if ext == ".nim" and name.startsWith(file):
|
||||
result.add(name)
|
||||
|
||||
proc collectImportModulesFromDir(dir: string, result: var seq[string]) =
|
||||
for kind, path in walkDir(dir):
|
||||
if kind in {pcFile, pcDir}:
|
||||
let (_, name, ext) = splitFile(path)
|
||||
if kind == pcFile:
|
||||
if ext == ".nim" and name.startsWith(partialPath):
|
||||
result.add(name)
|
||||
else:
|
||||
if name.startsWith(partialPath):
|
||||
result.add(name)
|
||||
|
||||
if '/' in partialPath:
|
||||
let parts = partialPath.split('/')
|
||||
let dir = parts[0]
|
||||
let file = parts[1]
|
||||
addModulesFromDir(currentModuleDir / dir, file, result)
|
||||
for searchPath in c.config.searchPaths:
|
||||
let searchDir = searchPath.string / dir
|
||||
addModulesFromDir(searchDir, file, result)
|
||||
else:
|
||||
collectImportModulesFromDir(currentModuleDir, result)
|
||||
for searchPath in c.config.searchPaths:
|
||||
collectImportModulesFromDir(searchPath.string, result)
|
||||
|
||||
proc suggestModuleNames(c: PContext, n: PNode) =
|
||||
var suggestions: Suggestions = @[]
|
||||
let partialPath = if n.kind == nkIdent: n.ident.s else: ""
|
||||
proc addModuleSuggestion(path: string) =
|
||||
var suggest = Suggest(
|
||||
section: ideSug,
|
||||
qualifiedPath: @[path],
|
||||
name: addr path,
|
||||
filePath: path,
|
||||
line: n.info.line.int,
|
||||
column: n.info.col.int,
|
||||
doc: "",
|
||||
quality: 100,
|
||||
contextFits: true,
|
||||
prefix: if partialPath.len > 0: prefixMatch(path, partialPath)
|
||||
else: PrefixMatch.None,
|
||||
symkind: byte skModule
|
||||
)
|
||||
suggestions.add(suggest)
|
||||
|
||||
let importCtx = extractImportContextFromAst(n, c.config.m.trackPos.col)
|
||||
var searchPath = ""
|
||||
if importCtx.baseDir.len > 0:
|
||||
searchPath = importCtx.baseDir & "/"
|
||||
|
||||
let possibleModules = findModuleFile(c, searchPath & importCtx.partialModule)
|
||||
for moduleName in possibleModules:
|
||||
if moduleName != c.module.name.s:
|
||||
addModuleSuggestion(moduleName)
|
||||
|
||||
produceOutput(suggestions, c.config)
|
||||
suggestQuit()
|
||||
|
||||
proc findImportStmtOnLine(n: PNode, line: uint16): PNode =
|
||||
if n.kind in {nkImportStmt, nkFromStmt} and n.info.line == line:
|
||||
return n
|
||||
for i in 0..<n.safeLen:
|
||||
let res = findImportStmtOnLine(n[i], line)
|
||||
if res != nil: return res
|
||||
return nil
|
||||
|
||||
template trySuggestModuleNames*(c: PContext, n: PNode) =
|
||||
if c.config.ideCmd == ideSug:
|
||||
let importNode = findImportStmtOnLine(n, c.config.m.trackPos.line)
|
||||
if importNode != nil:
|
||||
suggestModuleNames(c, importNode)
|
||||
|
||||
proc suggestExprNoCheck*(c: PContext, n: PNode) =
|
||||
# This keeps semExpr() from coming here recursively:
|
||||
if c.compilesContextId > 0: return
|
||||
|
||||
@@ -16,6 +16,7 @@ type
|
||||
caughtExceptions*: seq[PType]
|
||||
caughtExceptionsSet*: bool
|
||||
isDecl*: bool
|
||||
isGenericInstance*: bool
|
||||
|
||||
SuggestFileSymbolDatabase* = object
|
||||
lineInfo*: seq[TinyLineInfo]
|
||||
@@ -23,6 +24,7 @@ type
|
||||
caughtExceptions*: seq[seq[PType]]
|
||||
caughtExceptionsSet*: PackedBoolArray
|
||||
isDecl*: PackedBoolArray
|
||||
isGenericInstance*: PackedBoolArray
|
||||
fileIndex*: FileIndex
|
||||
trackCaughtExceptions*: bool
|
||||
isSorted*: bool
|
||||
@@ -82,6 +84,11 @@ proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair =
|
||||
s.caughtExceptionsSet[idx]
|
||||
else:
|
||||
false,
|
||||
isGenericInstance:
|
||||
if s.trackCaughtExceptions:
|
||||
s.isGenericInstance[idx]
|
||||
else:
|
||||
false,
|
||||
isDecl: s.isDecl[idx]
|
||||
)
|
||||
|
||||
@@ -90,6 +97,7 @@ proc reverse*(s: var SuggestFileSymbolDatabase) =
|
||||
s.sym.reverse()
|
||||
s.caughtExceptions.reverse()
|
||||
s.caughtExceptionsSet.reverse()
|
||||
s.isGenericInstance.reverse()
|
||||
s.isDecl.reverse()
|
||||
|
||||
proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase =
|
||||
@@ -99,6 +107,7 @@ proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions
|
||||
caughtExceptions: @[],
|
||||
caughtExceptionsSet: newPackedBoolArray(),
|
||||
isDecl: newPackedBoolArray(),
|
||||
isGenericInstance: newPackedBoolArray(),
|
||||
fileIndex: aFileIndex,
|
||||
trackCaughtExceptions: aTrackCaughtExceptions,
|
||||
isSorted: true
|
||||
@@ -119,6 +128,8 @@ func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int =
|
||||
result = cmp(s.lineInfo[i], s.lineInfo[j])
|
||||
if result == 0:
|
||||
result = cmp(s.isDecl[i], s.isDecl[j])
|
||||
if result == 0 and s.trackCaughtExceptions:
|
||||
result = cmp(s.isGenericInstance[i], s.isGenericInstance[j])
|
||||
|
||||
proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
|
||||
if i == j:
|
||||
@@ -133,6 +144,9 @@ proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
|
||||
var tmp3 = s.caughtExceptionsSet[i]
|
||||
s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j]
|
||||
s.caughtExceptionsSet[j] = tmp3
|
||||
var tmp6 = s.isGenericInstance[i]
|
||||
s.isGenericInstance[i] = s.isGenericInstance[j]
|
||||
s.isGenericInstance[j] = tmp6
|
||||
var tmp4 = s.isDecl[i]
|
||||
s.isDecl[i] = s.isDecl[j]
|
||||
s.isDecl[j] = tmp4
|
||||
@@ -196,12 +210,17 @@ proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) =
|
||||
if s.trackCaughtExceptions:
|
||||
s.caughtExceptions.add(v.caughtExceptions)
|
||||
s.caughtExceptionsSet.add(v.caughtExceptionsSet)
|
||||
s.isGenericInstance.add(v.isGenericInstance)
|
||||
s.isSorted = false
|
||||
|
||||
proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) =
|
||||
s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v)
|
||||
|
||||
proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int =
|
||||
proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo; isGenericInstance: bool): int =
|
||||
# if trackCaughtExceptions is false, then all records in the database are not generic instances, so
|
||||
# if we're searching for a generic instance, we find none
|
||||
if isGenericInstance and not s.trackCaughtExceptions:
|
||||
return -1
|
||||
doAssert(li.fileIndex == s.fileIndex)
|
||||
if not s.isSorted:
|
||||
s.sort()
|
||||
@@ -210,3 +229,17 @@ proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int =
|
||||
col: li.col
|
||||
)
|
||||
result = binarySearch(s.lineInfo, q, cmp)
|
||||
# if trackCaughtExceptions is false, then all records in the database are not generic instances, so
|
||||
# if we're a searching for a non-generic instance, then we're done, we return what we have found
|
||||
if not isGenericInstance and not s.trackCaughtExceptions:
|
||||
return
|
||||
# in this case trackCaughtExceptions is true, and the database contains both generic and non-generic instances, so we need
|
||||
# to check the isGenericInstance flag also
|
||||
if result != -1:
|
||||
# search through a sequence of equal lineInfos to find a matching isGenericInstance
|
||||
while result > 0 and s.isGenericInstance[result] != isGenericInstance and cmp(s.lineInfo[result], s.lineInfo[result - 1]) == 0:
|
||||
dec result
|
||||
while result < (s.lineInfo.len - 1) and s.isGenericInstance[result] != isGenericInstance and cmp(s.lineInfo[result], s.lineInfo[result + 1]) == 0:
|
||||
inc result
|
||||
if s.isGenericInstance[result] != isGenericInstance:
|
||||
result = -1
|
||||
|
||||
@@ -40,7 +40,7 @@ import closureiters, lambdalifting
|
||||
|
||||
type
|
||||
PTransCon = ref object # part of TContext; stackable
|
||||
mapping: Table[ItemId, PNode] # mapping from symbols to nodes
|
||||
mapping: TIdTable[PNode] # mapping from symbols to nodes
|
||||
owner: PSym # current owner
|
||||
forStmt: PNode # current for stmt
|
||||
forLoopBody: PNode # transformed for loop body
|
||||
@@ -78,7 +78,7 @@ proc newTransNode(kind: TNodeKind, n: PNode,
|
||||
|
||||
proc newTransCon(owner: PSym): PTransCon =
|
||||
assert owner != nil
|
||||
result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner)
|
||||
result = PTransCon(mapping: initIdTable[PNode](), owner: owner)
|
||||
|
||||
proc pushTransCon(c: PTransf, t: PTransCon) =
|
||||
t.next = c.transCon
|
||||
@@ -95,7 +95,7 @@ proc getCurrOwner(c: PTransf): PSym =
|
||||
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||
let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), info)
|
||||
r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||
incl(r.flags, sfFromGeneric)
|
||||
incl(r.flagsImpl, sfFromGeneric)
|
||||
let owner = getCurrOwner(c)
|
||||
result = newSymNode(r)
|
||||
|
||||
@@ -181,7 +181,7 @@ proc transformSym(c: PTransf, n: PNode): PNode =
|
||||
proc freshVar(c: PTransf; v: PSym): PNode =
|
||||
let owner = getCurrOwner(c)
|
||||
var newVar = copySym(v, c.idgen)
|
||||
incl(newVar.flags, sfFromGeneric)
|
||||
incl(newVar.flagsImpl, sfFromGeneric)
|
||||
setOwner(newVar, owner)
|
||||
result = newSymNode(newVar)
|
||||
|
||||
@@ -259,7 +259,8 @@ proc transformBlock(c: PTransf, n: PNode): PNode =
|
||||
var labl: PSym
|
||||
if c.inlining > 0:
|
||||
labl = newLabel(c, n[0])
|
||||
c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl)
|
||||
if n[0].kind != nkEmpty:
|
||||
c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl)
|
||||
else:
|
||||
labl =
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -328,7 +329,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
|
||||
if a.kind == nkSym:
|
||||
n[1] = transformSymAux(c, a)
|
||||
return n
|
||||
of nkProcDef: # todo optimize nosideeffects?
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
|
||||
result = newTransNode(n)
|
||||
let x = newSymNode(copySym(n[namePos].sym, c.idgen))
|
||||
c.transCon.mapping[n[namePos].sym.itemId] = x
|
||||
@@ -518,8 +519,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds, isAddr = false)
|
||||
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)
|
||||
not (isAddr and n.typ.kind == tyVar and n[0][0].typ.kind == tyRef)
|
||||
: # elimination is harmful to `for tuple unpack` because of newTupleAccess
|
||||
# it is also harmful to openArrayLoc (var openArray) for strings
|
||||
# addr ( deref ( x )) --> x
|
||||
@@ -552,7 +552,34 @@ proc transformConv(c: PTransf, n: PNode): PNode =
|
||||
# we don't include uint and uint64 here as these are no ordinal types ;-)
|
||||
if not isOrdinalType(source):
|
||||
# float -> int conversions. ugh.
|
||||
result = transformSons(c, n)
|
||||
# generate a range check:
|
||||
if dest.kind in tyInt..tyInt64:
|
||||
if dest.kind == tyInt64 or source.kind == tyInt64:
|
||||
result = newTransNode(nkChckRange64, n, 3)
|
||||
else:
|
||||
result = newTransNode(nkChckRange, n, 3)
|
||||
dest = skipTypes(n.typ, abstractVar)
|
||||
|
||||
if dest.size < source.size:
|
||||
let intType =
|
||||
if source.size == 4:
|
||||
getSysType(c.graph, n.info, tyInt32)
|
||||
else:
|
||||
getSysType(c.graph, n.info, tyInt64)
|
||||
result[0] =
|
||||
newTreeIT(n.kind, n.info, n.typ, n[0],
|
||||
newTreeIT(nkConv, n.info, intType,
|
||||
newNodeIT(nkType, n.info, intType), transform(c, n[1]))
|
||||
)
|
||||
|
||||
else:
|
||||
result[0] = transformSons(c, n)
|
||||
|
||||
result[1] = newIntTypeNode(firstOrd(c.graph.config, dest), dest)
|
||||
result[2] = newIntTypeNode(lastOrd(c.graph.config, dest), dest)
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
|
||||
elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n[1].typ) and
|
||||
lastOrd(c.graph.config, n[1].typ) <= lastOrd(c.graph.config, n.typ):
|
||||
# BUGFIX: simply leave n as it is; we need a nkConv node,
|
||||
@@ -768,7 +795,8 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
addVar(v, copyTree(n[i][j])) # declare new vars
|
||||
else:
|
||||
if n[i].kind == nkSym and isSimpleIteratorVar(c, iter, call, n[i].sym.owner):
|
||||
incl n[i].sym.flags, sfCursor
|
||||
# IC: review this solution again later
|
||||
incl n[i].sym.flagsImpl, sfCursor
|
||||
addVar(v, copyTree(n[i])) # declare new vars
|
||||
stmtList.add(v)
|
||||
|
||||
@@ -800,12 +828,25 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
t = formal.ast.typ # better use the type that actually has a destructor.
|
||||
elif t.destructor == nil and arg.typ.destructor != nil:
|
||||
t = arg.typ
|
||||
# generate a temporary and produce an assignment statement:
|
||||
var temp = newTemp(c, t, formal.info)
|
||||
#incl(temp.sym.flags, sfCursor)
|
||||
addVar(v, temp)
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
|
||||
newC.mapping[formal.itemId] = temp
|
||||
|
||||
if arg.kind in {nkDerefExpr, nkHiddenDeref} and
|
||||
arg[0].typ.skipTypes(abstractInst).kind != tyLent:
|
||||
# optimizes for `[]` # bug #24093
|
||||
# bug #25251: enforce a copy if the arg is a deref of a lent pointer
|
||||
# since the arg could be a temporary that will go out of scope
|
||||
var temp = newTemp(c, arg[0].typ, formal.info)
|
||||
addVar(v, temp)
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg[0], true))
|
||||
let newD = newDeref(temp)
|
||||
newD.typ() = t
|
||||
newC.mapping[formal.itemId] = newD
|
||||
else:
|
||||
# generate a temporary and produce an assignment statement:
|
||||
var temp = newTemp(c, t, formal.info)
|
||||
#incl(temp.sym.flags, sfCursor)
|
||||
addVar(v, temp)
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
|
||||
newC.mapping[formal.itemId] = temp
|
||||
of paVarAsgn:
|
||||
assert(skipTypes(formal.typ, abstractInst).kind in {tyVar, tyLent})
|
||||
newC.mapping[formal.itemId] = arg
|
||||
@@ -813,7 +854,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
of paViaIndirection:
|
||||
let t = formal.typ
|
||||
let vt = makeVarType(t.owner, t, c.idgen)
|
||||
vt.flags.incl tfVarIsPtr
|
||||
vt.incl tfVarIsPtr
|
||||
var temp = newTemp(c, vt, formal.info)
|
||||
addVar(v, temp)
|
||||
var addrExp = newNodeIT(nkHiddenAddr, formal.info, makeVarType(t.owner, t, c.idgen, tyPtr))
|
||||
@@ -957,6 +998,23 @@ proc transformCall(c: PTransf, n: PNode): PNode =
|
||||
else:
|
||||
result = s
|
||||
|
||||
proc transformBareExcept(c: PTransf, n: PNode): PNode =
|
||||
result = newTransNode(nkExceptBranch, n, 1)
|
||||
if isEmptyType(n[0].typ):
|
||||
result[0] = newNodeI(nkStmtList, n[0].info)
|
||||
else:
|
||||
result[0] = newNodeIT(nkStmtListExpr, n[0].info, n[0].typ)
|
||||
# Generating `raiseDefect()`
|
||||
let raiseDefectCall = callCodegenProc(c.graph, "raiseDefect", n[0].info)
|
||||
result[0].add raiseDefectCall
|
||||
if n[0].kind in {nkStmtList, nkStmtListExpr}:
|
||||
# flattens stmtList
|
||||
for son in n[0]:
|
||||
result[0].add son
|
||||
else:
|
||||
result[0].add n[0]
|
||||
result[0] = transform(c, result[0])
|
||||
|
||||
proc transformExceptBranch(c: PTransf, n: PNode): PNode =
|
||||
if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
|
||||
let excTypeNode = n[0][1]
|
||||
@@ -985,6 +1043,9 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
|
||||
# Replace the `Exception as foobar` with just `Exception`.
|
||||
result[0] = transform(c, n[0][1])
|
||||
result[1] = actions
|
||||
elif n.len == 1 and
|
||||
noPanicOnExcept notin c.graph.config.legacyFeatures:
|
||||
result = transformBareExcept(c, n)
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user