mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
705 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c00e8e71e0 | ||
|
|
a073e225c8 | ||
|
|
140d641443 | ||
|
|
cc779a42ce | ||
|
|
26a4b137c6 | ||
|
|
d7544ec3db | ||
|
|
767a901267 | ||
|
|
36f8cccda4 | ||
|
|
d6bc8699b9 | ||
|
|
7db4376cda | ||
|
|
a85c635da8 | ||
|
|
3e18c48cda | ||
|
|
3455623500 | ||
|
|
8daac1b061 | ||
|
|
7071e3a080 | ||
|
|
98075688f3 | ||
|
|
5abf9a1d77 | ||
|
|
ab053fd56a | ||
|
|
598de35d45 | ||
|
|
a88b275bc3 | ||
|
|
e57b207ff8 | ||
|
|
b9951e8c87 | ||
|
|
9ad0ada8e4 | ||
|
|
30eb2b394e | ||
|
|
6a1694b28b | ||
|
|
8f5451832e | ||
|
|
cd65b5e5f8 | ||
|
|
80a6005f55 | ||
|
|
cd72be29c6 | ||
|
|
1425654e9d | ||
|
|
1463d811fb | ||
|
|
d372a0030d | ||
|
|
a6e729ed5d | ||
|
|
c5f2d84ac7 | ||
|
|
24e5e9875c | ||
|
|
4e977767a7 | ||
|
|
df589bcce1 | ||
|
|
e690ff35a2 | ||
|
|
e3f4c3d417 | ||
|
|
efcb6b4b11 | ||
|
|
5eee3e06fc | ||
|
|
dbbd2c8002 | ||
|
|
59e49ded2d | ||
|
|
4bf12a086d | ||
|
|
b1bfba9a31 | ||
|
|
a798356838 | ||
|
|
1d7170f0ed | ||
|
|
eaa8c7ca7a | ||
|
|
5f8e08031d | ||
|
|
c2a14cb3fc | ||
|
|
b3a62931cd | ||
|
|
b213da9937 | ||
|
|
6eb3e3134e | ||
|
|
1e2d98a3e0 | ||
|
|
55b5068d20 | ||
|
|
c17e48c9c5 | ||
|
|
11713ef018 | ||
|
|
b522d1fe24 | ||
|
|
dd94d98110 | ||
|
|
2f399807cb | ||
|
|
5cecd81a05 | ||
|
|
526e48b2ed | ||
|
|
a16f126614 | ||
|
|
8b815cc857 | ||
|
|
d43672833a | ||
|
|
fcb8461efa | ||
|
|
871cd4b9db | ||
|
|
fa78d937dc | ||
|
|
b302b3102e | ||
|
|
eb3aa70ffa | ||
|
|
72a2fe5125 | ||
|
|
e582f1ccb3 | ||
|
|
0e1a699937 | ||
|
|
494b5486ba | ||
|
|
33817f2c30 | ||
|
|
f44c49c5bd | ||
|
|
3dd5e11480 | ||
|
|
716ff93161 | ||
|
|
1ac5aa951d | ||
|
|
d112874078 | ||
|
|
61da33f31b | ||
|
|
e15aa20059 | ||
|
|
8d7b7ff861 | ||
|
|
d389310bb9 | ||
|
|
1ae6deda61 | ||
|
|
6d38eafda1 | ||
|
|
845be91df5 | ||
|
|
fa003a00ee | ||
|
|
45f1b19942 | ||
|
|
96917755cd | ||
|
|
8a7a776034 | ||
|
|
e33d96ad4e | ||
|
|
5a2cd98867 | ||
|
|
a1b310d186 | ||
|
|
f97c0cced0 | ||
|
|
2c964268f8 | ||
|
|
a715aeb716 | ||
|
|
d57200bc6f | ||
|
|
fbb9ce4d5c | ||
|
|
76735147b3 | ||
|
|
c2d297cc3c | ||
|
|
4886534ed3 | ||
|
|
2ed16bf2d4 | ||
|
|
c3d043e980 | ||
|
|
0cdca3a0cd | ||
|
|
5e22f6ca58 | ||
|
|
f8d538f4ad | ||
|
|
8efdc313a3 | ||
|
|
49cc3c5746 | ||
|
|
865a2f895e | ||
|
|
82e77a13f3 | ||
|
|
fca71c4bb4 | ||
|
|
3d4ec68e54 | ||
|
|
54bd380011 | ||
|
|
55905604da | ||
|
|
fd64402e47 | ||
|
|
c696ec92db | ||
|
|
4bc45bb6bc | ||
|
|
9f35ede1a8 | ||
|
|
51fa2fada1 | ||
|
|
aed04c6405 | ||
|
|
d566d21730 | ||
|
|
5c4c8c44dc | ||
|
|
47f1ab7b97 | ||
|
|
710801d880 | ||
|
|
39d0eb3a58 | ||
|
|
71dde8f7cf | ||
|
|
683a1213db | ||
|
|
3ae3050b80 | ||
|
|
bd829c093d | ||
|
|
5e20e935dd | ||
|
|
106c7d2afb | ||
|
|
755a15d8d4 | ||
|
|
a5a6a4d39e | ||
|
|
3120c6e839 | ||
|
|
0d255b4ea1 | ||
|
|
e9067c03c6 | ||
|
|
999d29d2a0 | ||
|
|
08d37c2d7e | ||
|
|
4f78a4dd3e | ||
|
|
bb82d46ef1 | ||
|
|
567900be76 | ||
|
|
cb899ccf25 | ||
|
|
710fd14393 | ||
|
|
499ad5b63c | ||
|
|
8ab6cf1ca8 | ||
|
|
577ffbc57c | ||
|
|
e918a762f7 | ||
|
|
d56000eaff | ||
|
|
cc7f76a247 | ||
|
|
adf3e3ceb5 | ||
|
|
344feb0dd1 | ||
|
|
dc6058fb9b | ||
|
|
bbf32118a2 | ||
|
|
b2cb5f55ac | ||
|
|
7083d397f3 | ||
|
|
3ddacd90d1 | ||
|
|
cb3e5d3f28 | ||
|
|
12f2c36aca | ||
|
|
a12cb273b3 | ||
|
|
1bbbb2f46b | ||
|
|
28cef86a09 | ||
|
|
9ac5666271 | ||
|
|
70ab7a53f3 | ||
|
|
70d771d1a9 | ||
|
|
45aa0a4725 | ||
|
|
d4c1cc7a34 | ||
|
|
131a0328b5 | ||
|
|
7335495db7 | ||
|
|
24f3272db3 | ||
|
|
3911c90d7b | ||
|
|
7c44af4e22 | ||
|
|
d49d7ba90c | ||
|
|
26f3dfff75 | ||
|
|
271632a72d | ||
|
|
0d02bee23f | ||
|
|
c39a0139fc | ||
|
|
06464fe2ff | ||
|
|
8eaf98d64f | ||
|
|
2a4755ddf2 | ||
|
|
814e929eeb | ||
|
|
6f00b46c4b | ||
|
|
665480372e | ||
|
|
2fa314a89d | ||
|
|
bd63124a00 | ||
|
|
b67d0a64b4 | ||
|
|
09109c55c9 | ||
|
|
c1745b5924 | ||
|
|
a7394440b9 | ||
|
|
42e4190e1a | ||
|
|
36b339bbcf | ||
|
|
fb2cc06557 | ||
|
|
ac66f6ce7a | ||
|
|
f4d99c8d56 | ||
|
|
d097028307 | ||
|
|
7fbf42960b | ||
|
|
526dbf0cf2 | ||
|
|
1c19d5c72e | ||
|
|
413395866f | ||
|
|
3ad16028aa | ||
|
|
b47747d318 | ||
|
|
5667fbfbf3 | ||
|
|
d4b58b0b06 | ||
|
|
ef356cfd4b | ||
|
|
e374759f29 | ||
|
|
0de70dc7f3 | ||
|
|
2f15b67fd9 | ||
|
|
e03667e920 | ||
|
|
11c048b06f | ||
|
|
22de3ae53e | ||
|
|
3be2514683 | ||
|
|
57658b685c | ||
|
|
5f6ff1f051 | ||
|
|
7e6f4723c3 | ||
|
|
4a1151d6b4 | ||
|
|
9994b019c5 | ||
|
|
bb930aef2d | ||
|
|
be66aedb9e | ||
|
|
4bc305db1e | ||
|
|
fdb1f4451d | ||
|
|
02497c9533 | ||
|
|
0acdce502c | ||
|
|
fe1d4a6232 | ||
|
|
c4c44d10df | ||
|
|
55bb60a56b | ||
|
|
ef8b8317ff | ||
|
|
fff127bec7 | ||
|
|
7b5289a1a4 | ||
|
|
3a0d09c1c5 | ||
|
|
3ab6012c81 | ||
|
|
add124e07a | ||
|
|
8834f3e74d | ||
|
|
489dd89a45 | ||
|
|
ba9effc8a1 | ||
|
|
0d93fdf607 | ||
|
|
b8cec0c714 | ||
|
|
4793fc0fc1 | ||
|
|
e1b44666b0 | ||
|
|
194d21a6e4 | ||
|
|
1b80f7126a | ||
|
|
1dc85802e2 | ||
|
|
9e081579bb | ||
|
|
30dad5dfa1 | ||
|
|
6d32066055 | ||
|
|
86e5545c25 | ||
|
|
f4e80d51ff | ||
|
|
1db305af1c | ||
|
|
805b4e2dc2 | ||
|
|
e8e9948223 | ||
|
|
75441dcdc3 | ||
|
|
d30565f810 | ||
|
|
5c916b645b | ||
|
|
9ad184841e | ||
|
|
7536d7f94a | ||
|
|
ee0d3f9b34 | ||
|
|
d3cf815dbe | ||
|
|
b477c2adcc | ||
|
|
78d4aa3d01 | ||
|
|
855f5c8b81 | ||
|
|
7ab0f3beec | ||
|
|
ba8c9a9d1e | ||
|
|
87c1200c93 | ||
|
|
d46e71bea3 | ||
|
|
4cf2658235 | ||
|
|
5fa48e97b6 | ||
|
|
562fb0d14b | ||
|
|
320ecc69a0 | ||
|
|
106cf7a9f7 | ||
|
|
7221c6f156 | ||
|
|
4a7cdaecb4 | ||
|
|
81d58d2cc8 | ||
|
|
1080f5eba2 | ||
|
|
18e62ad136 | ||
|
|
f03aa1d0b5 | ||
|
|
ef63d47ecd | ||
|
|
0a58af8cbf | ||
|
|
8359f9683a | ||
|
|
c3425c05e9 | ||
|
|
3fa378533a | ||
|
|
4850bb5a59 | ||
|
|
037f536e7e | ||
|
|
287fbc5fb5 | ||
|
|
f553288db3 | ||
|
|
e0e386314f | ||
|
|
1de6a7a023 | ||
|
|
d1db4c29fa | ||
|
|
a488067a41 | ||
|
|
35ff70f36c | ||
|
|
0b3ddd4e47 | ||
|
|
b56df5c07f | ||
|
|
569ccc50ff | ||
|
|
d51bc084fd | ||
|
|
281016a802 | ||
|
|
19d1fe7af3 | ||
|
|
e70992d291 | ||
|
|
f0f3904ff0 | ||
|
|
f1ac979184 | ||
|
|
db77c98471 | ||
|
|
11c8dfc9b3 | ||
|
|
c0994c2dbd | ||
|
|
1c2ccfad08 | ||
|
|
dce714b259 | ||
|
|
8216d7dd46 | ||
|
|
49a108b302 | ||
|
|
808c9c6c2a | ||
|
|
be1844541c | ||
|
|
62869a5c68 | ||
|
|
b10d3cd98d | ||
|
|
e2ea9140ac | ||
|
|
0e555ade31 | ||
|
|
576f4a7348 | ||
|
|
3ebe24977c | ||
|
|
b02c1dd6ca | ||
|
|
993fcf5bda | ||
|
|
91987f8eb5 | ||
|
|
2f817ee5b4 | ||
|
|
3f9e16594f | ||
|
|
c1a82aa5c5 | ||
|
|
5ed44e1ec4 | ||
|
|
0d3bde95f5 | ||
|
|
7d9fe106ec | ||
|
|
1aff402998 | ||
|
|
14a9929464 | ||
|
|
f16b94a9d7 | ||
|
|
17915d93bf | ||
|
|
50d435cd39 | ||
|
|
f9280090f6 | ||
|
|
db1ce4ff12 | ||
|
|
3b377937de | ||
|
|
9471b5f964 | ||
|
|
9ddd768cce | ||
|
|
ecc6ab7ee0 | ||
|
|
1b132ddaa2 | ||
|
|
6ec10a4c91 | ||
|
|
54d7c9fdf8 | ||
|
|
2e987cb75a | ||
|
|
148ff74c93 | ||
|
|
a15db5d60b | ||
|
|
dfa0d2569e | ||
|
|
7616e6ee2b | ||
|
|
145e002c74 | ||
|
|
86ff37fab8 | ||
|
|
31ba1046fc | ||
|
|
c513e37a70 | ||
|
|
d9a24b9b81 | ||
|
|
1ad618d96c | ||
|
|
57296a5139 | ||
|
|
83a5865024 | ||
|
|
aedb4c26de | ||
|
|
7cb59efd4b | ||
|
|
2f109595e9 | ||
|
|
4d2ebbb877 | ||
|
|
3a1adf7d66 | ||
|
|
210b10dd0d | ||
|
|
41ec894cb0 | ||
|
|
d139d99946 | ||
|
|
57de460437 | ||
|
|
b35942ef83 | ||
|
|
d195877615 | ||
|
|
427ad17161 | ||
|
|
9616762cfe | ||
|
|
ce0909f047 | ||
|
|
faa59b1e1f | ||
|
|
e422b3c860 | ||
|
|
d52b1d848e | ||
|
|
47635d3031 | ||
|
|
cb40f11e6c | ||
|
|
4b761295e7 | ||
|
|
4ce3a68e79 | ||
|
|
4546f5dfe5 | ||
|
|
43a3de9077 | ||
|
|
3e44d5742f | ||
|
|
942c378659 | ||
|
|
20037a4749 | ||
|
|
f718f295df | ||
|
|
c6c85f84db | ||
|
|
beaac609ab | ||
|
|
88114948c4 | ||
|
|
d137a3b52a | ||
|
|
ac7b8b678c | ||
|
|
eb2f0353bc | ||
|
|
3ad2e7df1c | ||
|
|
ede03782a3 | ||
|
|
5e529b3bfa | ||
|
|
a345cde26e | ||
|
|
db41f04ab0 | ||
|
|
f524d60fa1 | ||
|
|
29a43124cf | ||
|
|
2054f1c3a9 | ||
|
|
2bb7277a61 | ||
|
|
6d21637245 | ||
|
|
647d9611ae | ||
|
|
e8d0f1c3ae | ||
|
|
0de9e6bbf3 | ||
|
|
8c3b0e9b48 | ||
|
|
7ea8f1d5dd | ||
|
|
88388040db | ||
|
|
77beb15214 | ||
|
|
0750210a50 | ||
|
|
3ac2d81601 | ||
|
|
b4d9be0f8a | ||
|
|
0a19d78b22 | ||
|
|
4937aa952b | ||
|
|
edb64bcff4 | ||
|
|
d90581c677 | ||
|
|
13b7e4e0e5 | ||
|
|
44f377edaf | ||
|
|
20dfdba2df | ||
|
|
894a19c6ed | ||
|
|
606e9941d0 | ||
|
|
cca5e5ffb9 | ||
|
|
fda8b6f193 | ||
|
|
2e12d3e26b | ||
|
|
5679f2b84c | ||
|
|
db8f6f4e38 | ||
|
|
93cace160e | ||
|
|
71801c2b8f | ||
|
|
e0ad71a912 | ||
|
|
7b1c448744 | ||
|
|
5139a2ec37 | ||
|
|
9b5f310b9e | ||
|
|
21d941c16a | ||
|
|
8bba04078c | ||
|
|
d3af0882cf | ||
|
|
b2d7761975 | ||
|
|
65c412e3f0 | ||
|
|
744a99d75c | ||
|
|
1d6fd30afb | ||
|
|
64b27edd3a | ||
|
|
49e11d9dcc | ||
|
|
18462b1026 | ||
|
|
f09101416f | ||
|
|
a7682325a2 | ||
|
|
a3bbb71240 | ||
|
|
a4f9413a65 | ||
|
|
a8d0dda833 | ||
|
|
750a33cbf1 | ||
|
|
64accd1c57 | ||
|
|
6514eaa8e0 | ||
|
|
7ee00d86b1 | ||
|
|
ba4fb21032 | ||
|
|
ea91cfb305 | ||
|
|
d29237b3c7 | ||
|
|
7fad6e5c70 | ||
|
|
47fa7506ab | ||
|
|
ebecfbc7a5 | ||
|
|
ce72b564bc | ||
|
|
fcc383d899 | ||
|
|
9f3d1b1290 | ||
|
|
ccc706ff12 | ||
|
|
1dedad5620 | ||
|
|
80fc749835 | ||
|
|
134b1890d5 | ||
|
|
0f3d6b5a52 | ||
|
|
7ca55f7de6 | ||
|
|
b97d603cd0 | ||
|
|
2ab948ce53 | ||
|
|
0a212f97a5 | ||
|
|
c7c3362cc8 | ||
|
|
1edae67efd | ||
|
|
211ef26f29 | ||
|
|
85d8ee2cee | ||
|
|
767fec1640 | ||
|
|
a1b7d2278a | ||
|
|
0d4d70f15c | ||
|
|
929cb4d601 | ||
|
|
25fe4124e6 | ||
|
|
f552618d6b | ||
|
|
3d18b204dd | ||
|
|
b86060b2ba | ||
|
|
8cc49f221e | ||
|
|
6dd30a6d86 | ||
|
|
1133f20fe2 | ||
|
|
ead7e20926 | ||
|
|
c507ced51e | ||
|
|
8e35b3d577 | ||
|
|
b3e1892eb7 | ||
|
|
8f760080c5 | ||
|
|
b880cdff49 | ||
|
|
0e5c18a73a | ||
|
|
086a3e42eb | ||
|
|
bf9ee00998 | ||
|
|
e43a51fcf3 | ||
|
|
20446b437b | ||
|
|
a9385a6b4a | ||
|
|
4d20227438 | ||
|
|
546af8c571 | ||
|
|
40f88da90b | ||
|
|
7e055413f9 | ||
|
|
171b916613 | ||
|
|
244565397d | ||
|
|
ef060e8184 | ||
|
|
108410ac34 | ||
|
|
f47b27d532 | ||
|
|
8c55e2999b | ||
|
|
7ebb042f79 | ||
|
|
5997324709 | ||
|
|
9cb0fcf319 | ||
|
|
c2abcb06cc | ||
|
|
2900987c2f | ||
|
|
b96c501836 | ||
|
|
d5ba14db61 | ||
|
|
2dcc7195da | ||
|
|
ef3c0bec1c | ||
|
|
af3fd5a010 | ||
|
|
73095e2abb | ||
|
|
6048367a9f | ||
|
|
b0e1bc02c6 | ||
|
|
38fdf13982 | ||
|
|
6128ef53c5 | ||
|
|
09f36f5198 | ||
|
|
2beea72810 | ||
|
|
1aaff9dc48 | ||
|
|
656706026b | ||
|
|
f2d26f2973 | ||
|
|
b50babd0ae | ||
|
|
ab4d044a81 | ||
|
|
908e971732 | ||
|
|
609bf3d7c8 | ||
|
|
0eb508e434 | ||
|
|
a8718d8a9e | ||
|
|
b7925bf5c9 | ||
|
|
446e5fbbb3 | ||
|
|
cb3f6fdc66 | ||
|
|
4d6be458a0 | ||
|
|
c7f2541914 | ||
|
|
b63b5c930e | ||
|
|
266cc69f19 | ||
|
|
761b927e47 | ||
|
|
9493e67291 | ||
|
|
bdccc9fef9 | ||
|
|
1252070193 | ||
|
|
d372ad3ee6 | ||
|
|
76a98fee65 | ||
|
|
d696ef5ad7 | ||
|
|
ee3650b29e | ||
|
|
b14043c39e | ||
|
|
9c2d2773ec | ||
|
|
28a116a477 | ||
|
|
5606702e6d | ||
|
|
44f059c75e | ||
|
|
016aa1d98c | ||
|
|
fcf2dcf099 | ||
|
|
641e34bcb2 | ||
|
|
a852b2e9cf | ||
|
|
476e032004 | ||
|
|
4186529ff7 | ||
|
|
21ff10b882 | ||
|
|
02a10ec379 | ||
|
|
1314ea7516 | ||
|
|
f22e5067c5 | ||
|
|
eecf12c4b5 | ||
|
|
ce1ba91573 | ||
|
|
cf1ab2a30b | ||
|
|
f4a9b258c3 | ||
|
|
0ece98620f | ||
|
|
ddce555998 | ||
|
|
871e4af6ef | ||
|
|
9c40dd2406 | ||
|
|
161f50643a | ||
|
|
c6e2dc1919 | ||
|
|
ea39c600ab | ||
|
|
9810b8cf7f | ||
|
|
ebbad9e960 | ||
|
|
c004483581 | ||
|
|
f7ed293fbd | ||
|
|
802d57c237 | ||
|
|
3b9999b93c | ||
|
|
fa5e7dc44a | ||
|
|
71dc929ad7 | ||
|
|
02be212dae | ||
|
|
3a08e2e6ac | ||
|
|
055a00a6ef | ||
|
|
71439c2891 | ||
|
|
f3a4cc584e | ||
|
|
deaf684375 | ||
|
|
4b76037e5f | ||
|
|
5592d1ef2c | ||
|
|
b169dad1e5 | ||
|
|
5491e0c274 | ||
|
|
4ee70165f1 | ||
|
|
ec3bca8fab | ||
|
|
e45eb39ef7 | ||
|
|
4533e894ad | ||
|
|
ebdff1c7d3 | ||
|
|
71f2e1a502 | ||
|
|
4a94f3606e | ||
|
|
8cf5643621 | ||
|
|
b562e1e6d8 | ||
|
|
ba698d37c3 | ||
|
|
d0c62fa169 | ||
|
|
365a753eed | ||
|
|
53c15f24e9 | ||
|
|
b74d49c037 | ||
|
|
10328e50a5 | ||
|
|
85dbfc68b5 | ||
|
|
07233ceca0 | ||
|
|
724866b14f | ||
|
|
e92d7681bb | ||
|
|
62f1ad54ae | ||
|
|
79ac242c72 | ||
|
|
acfa7849d3 | ||
|
|
a929e513fa | ||
|
|
c34950f8f5 | ||
|
|
34b78be396 | ||
|
|
44736d26cd | ||
|
|
f37ecbb994 | ||
|
|
1d80dc7df6 | ||
|
|
a154950570 | ||
|
|
e5d0907a42 | ||
|
|
ca82b4ea16 | ||
|
|
c2bcfd8cd9 | ||
|
|
afc30ca879 | ||
|
|
2844ac8b5e | ||
|
|
3e82a315fc | ||
|
|
aec5a4c474 | ||
|
|
8e0f336f6d | ||
|
|
a593e40ad6 | ||
|
|
77093bf7b9 | ||
|
|
43f29842fc | ||
|
|
91abf35442 | ||
|
|
560fa9a1fe | ||
|
|
220b450489 | ||
|
|
8f79a124c9 | ||
|
|
69c193e5e2 | ||
|
|
0032322ea8 | ||
|
|
f0ae1ed544 | ||
|
|
897dff69dd | ||
|
|
4754c51f1b | ||
|
|
0f226c0e48 | ||
|
|
20b011de19 | ||
|
|
380dafcc32 | ||
|
|
265a340e80 | ||
|
|
6ad246b215 | ||
|
|
63d29ddd69 | ||
|
|
c136ebf1ed | ||
|
|
4fa86422c0 | ||
|
|
48de0d0cf4 | ||
|
|
b54b03d04f | ||
|
|
175a83c2de | ||
|
|
d76458a6cd | ||
|
|
418e54452b | ||
|
|
f9477396a6 | ||
|
|
ed7c6cdc98 | ||
|
|
135b677704 | ||
|
|
9cb06d357e | ||
|
|
0d6b994bee | ||
|
|
d2d849db8e | ||
|
|
24b6378382 | ||
|
|
2f547afb0f | ||
|
|
9dc1f2dd0f | ||
|
|
202b190473 | ||
|
|
b0a98cc01e | ||
|
|
2621f78b68 | ||
|
|
65223e6f59 | ||
|
|
91e4381a20 | ||
|
|
8c4b7129b5 | ||
|
|
1af21c2668 | ||
|
|
938f5c5e99 | ||
|
|
a0da74ed4c | ||
|
|
ea8aafa7c9 | ||
|
|
512517c223 | ||
|
|
1a99bcd6ab | ||
|
|
2a0d8a9a06 | ||
|
|
c694d8e4fd | ||
|
|
16f42084d3 | ||
|
|
3f51b6f73d | ||
|
|
c33ab0ba38 | ||
|
|
1ed54b7718 | ||
|
|
f05387045d | ||
|
|
be06446ffe | ||
|
|
1bb117cd7a | ||
|
|
420b0c14eb | ||
|
|
4c073cffbe | ||
|
|
5e016e4466 | ||
|
|
75205fee93 | ||
|
|
4d683fc689 | ||
|
|
16bc546aea | ||
|
|
686c75cef0 | ||
|
|
a37a83cbff | ||
|
|
814d3e6818 | ||
|
|
4898b054ce | ||
|
|
baa577e9e8 | ||
|
|
c71192043b | ||
|
|
3575f2bf9c | ||
|
|
31d3606fe8 | ||
|
|
6ec9c7f683 | ||
|
|
63b4b3c5b8 | ||
|
|
0c6f14af04 | ||
|
|
a80f1a324f | ||
|
|
1c7fd71720 | ||
|
|
d5719c47dc | ||
|
|
2e4ba4ad93 | ||
|
|
b865f6a5f0 | ||
|
|
72ca444122 | ||
|
|
ecf9efa397 | ||
|
|
51ced0d684 | ||
|
|
c06623bf8c | ||
|
|
2315b01ae6 | ||
|
|
4fc9f0c3a3 | ||
|
|
115cec1745 | ||
|
|
0630c649c6 | ||
|
|
ff5ed1dbb1 | ||
|
|
7d83dfd0d1 | ||
|
|
3936071772 |
69
.github/stale.yml
vendored
69
.github/stale.yml
vendored
@@ -1,69 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 365
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 30
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- ARC
|
||||
- bounty
|
||||
- Codegen
|
||||
- Crash
|
||||
- Generics
|
||||
- High Priority
|
||||
- Macros
|
||||
- Next release
|
||||
- Showstopper
|
||||
- Static[T]
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This pull request has been automatically marked as stale because it has not had
|
||||
recent activity.
|
||||
If you think it is still a valid PR, please rebase it on the latest devel;
|
||||
otherwise it will be closed. Thank you for your contributions.
|
||||
|
||||
# Comment to post when removing the stale label.
|
||||
# unmarkComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
# closeComment: >
|
||||
# Your comment here.
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 20
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: pulls
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
23
.github/workflows/bisects.yml
vendored
Normal file
23
.github/workflows/bisects.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901
|
||||
name: issue comments bisects
|
||||
on:
|
||||
issue_comment:
|
||||
types: created
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# nimrun-action requires Nim installed.
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: 'devel'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install --no-install-recommends -yq valgrind
|
||||
|
||||
- uses: juancarlospaco/nimrun-action@nim
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
115
.github/workflows/ci_bench.yml
vendored
Normal file
115
.github/workflows/ci_bench.yml
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
name: Benchmarks CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Build csourcesAny'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'Build koch'
|
||||
shell: bash
|
||||
run: nim c koch
|
||||
|
||||
- name: 'Build Nim'
|
||||
shell: bash
|
||||
run: ./koch boot -d:release -d:nimStrictMode --lib:lib
|
||||
|
||||
- name: 'Build Nimble'
|
||||
shell: bash
|
||||
run: ./koch nimble
|
||||
|
||||
- name: 'Action'
|
||||
shell: bash
|
||||
run: nim c -r -d:release ci/action.nim
|
||||
|
||||
- name: 'Checkout minimize'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'nim-lang/ci_bench'
|
||||
path: minimize
|
||||
|
||||
- name: 'Run minimize benchmarks'
|
||||
shell: bash
|
||||
run: ./minimize/minimize ci-bench
|
||||
|
||||
- name: 'Restore minimize cached database'
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: minimize.csv
|
||||
key: minimize-db-key
|
||||
|
||||
- name: 'Update minimize db'
|
||||
shell: bash
|
||||
run: ./minimize/minimize update-db
|
||||
|
||||
- name: 'Save minimize cached database'
|
||||
if: |
|
||||
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
||||
matrix.target == 'linux'
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: minimize.csv
|
||||
key: minimize-db-key
|
||||
|
||||
- name: 'Generate minimize report'
|
||||
shell: bash
|
||||
run: ./minimize/minimize generate-report
|
||||
|
||||
- name: 'Archive minimize report'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: minimize-report
|
||||
path: |
|
||||
minimize/minimize.html
|
||||
minimize/minimize.csv
|
||||
|
||||
# Requires additional permissions, see:
|
||||
# https://github.com/nim-lang/Nim/actions/runs/4778177321/jobs/8494423792?pr=21566
|
||||
# - name: 'Publish HTML report'
|
||||
# uses: rossjrw/pr-preview-action@v1
|
||||
# with:
|
||||
# source-dir: minimize
|
||||
# umbrella-dir: minimize
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract md summary
|
||||
run: |
|
||||
cat minimize/summary.md >> $GITHUB_STEP_SUMMARY
|
||||
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-11
|
||||
os: macos-12
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
8
.github/workflows/ci_packages.yml
vendored
8
.github/workflows/ci_packages.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-11]
|
||||
os: [ubuntu-20.04, macos-12]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
@@ -32,10 +32,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
|
||||
6
.github/workflows/ci_publish.yml
vendored
6
.github/workflows/ci_publish.yml
vendored
@@ -21,10 +21,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v3
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
|
||||
25
.github/workflows/stale.yml
vendored
Normal file
25
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# https://github.com/actions/stale#usage
|
||||
name: Stale pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Midnight.
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
days-before-pr-stale: 365
|
||||
days-before-pr-close: 30
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
exempt-pr-labels: "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]"
|
||||
exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority"
|
||||
stale-pr-message: >
|
||||
This pull request is stale because it has been open for 1 year with no activity.
|
||||
Contribute more commits on the pull request and rebase it on the latest devel,
|
||||
or it will be closed in 30 days. Thank you for your contributions.
|
||||
close-pr-message: >
|
||||
This pull request has been marked as stale and closed due to inactivity after 395 days.
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -110,3 +110,5 @@ htmldocs
|
||||
nimdoc.out.css
|
||||
# except here:
|
||||
!/nimdoc/testproject/expected/*
|
||||
pkgs/
|
||||
/compiler/compiler/
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
# xxx unused, out of date
|
||||
|
||||
image: ubuntu:18.04
|
||||
|
||||
stages:
|
||||
- pre-build
|
||||
- build
|
||||
- deploy
|
||||
- test
|
||||
|
||||
.linux_set_path: &linux_set_path_def
|
||||
before_script:
|
||||
- export PATH=$(pwd)/bin${PATH:+:$PATH}
|
||||
tags:
|
||||
- linux
|
||||
|
||||
.windows_set_path: &win_set_path_def
|
||||
before_script:
|
||||
- set PATH=%CD%\bin;%PATH%
|
||||
tags:
|
||||
- windows
|
||||
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
script:
|
||||
- ci\build.bat
|
||||
artifacts:
|
||||
paths:
|
||||
- bin\nim.exe
|
||||
- bin\nimd.exe
|
||||
- compiler\nim.exe
|
||||
- koch.exe
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- windows
|
||||
|
||||
deploy-windows:
|
||||
stage: deploy
|
||||
script:
|
||||
- koch.exe winrelease
|
||||
artifacts:
|
||||
paths:
|
||||
- build/*.exe
|
||||
- build/*.zip
|
||||
expire_in: 1 week
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
|
||||
|
||||
|
||||
test-windows:
|
||||
stage: test
|
||||
<<: *win_set_path_def
|
||||
script:
|
||||
- call ci\deps.bat
|
||||
- nim c testament\tester
|
||||
- testament\tester.exe all
|
||||
tags:
|
||||
- windows
|
||||
- fast
|
||||
@@ -23,16 +23,16 @@ jobs:
|
||||
vmImage: 'ubuntu-20.04'
|
||||
CPU: amd64
|
||||
# regularly breaks, refs bug #17325
|
||||
Linux_i386:
|
||||
# on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
|
||||
# g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||
vmImage: 'ubuntu-18.04'
|
||||
CPU: i386
|
||||
# Linux_i386:
|
||||
# # on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
|
||||
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-11'
|
||||
vmImage: 'macOS-12'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-11'
|
||||
vmImage: 'macOS-12'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
|
||||
46
changelog.md
46
changelog.md
@@ -10,7 +10,12 @@
|
||||
|
||||
|
||||
[//]: # "Additions:"
|
||||
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
|
||||
|
||||
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to system, which doesn't initalize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
@@ -22,10 +27,45 @@
|
||||
|
||||
|
||||
|
||||
- An experimental option `genericsOpenSym` has been added to allow captured
|
||||
symbols in generic routine bodies to be replaced by symbols injected locally
|
||||
by templates/macros at instantiation time. `bind` may be used to keep the
|
||||
captured symbols over the injected ones regardless of enabling the option.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`genericsOpenSym` needs to be enabled, and a warning is given in the case
|
||||
where an injected symbol would replace a captured symbol not bound by `bind`
|
||||
and the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped) =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:genericsOpenSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
{.experimental: "genericsOpenSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
```
|
||||
|
||||
## Compiler changes
|
||||
|
||||
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
|
||||
@@ -1,367 +1,330 @@
|
||||
# v2.0.0 - yyyy-mm-dd
|
||||
# v2.0.0 - 2023-08-01
|
||||
|
||||
Version 2.0 is a big milestone with too many changes to list them all here.
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`.
|
||||
For a full list see [details](changelog_2_0_0_details.html).
|
||||
|
||||
- `addr` is now available for all addressable locations,
|
||||
`unsafeAddr` is now deprecated and an alias for `addr`.
|
||||
|
||||
- Certain definitions from the default `system` module have been moved to
|
||||
the following new modules:
|
||||
## New features
|
||||
|
||||
- `std/syncio`
|
||||
- `std/assertions`
|
||||
- `std/formatfloat`
|
||||
- `std/objectdollar`
|
||||
- `std/widestrs`
|
||||
- `std/typedthreads`
|
||||
- `std/sysatomics`
|
||||
### Better tuple unpacking
|
||||
|
||||
In the future, these definitions will be removed from the `system` module,
|
||||
and their respective modules will have to be imported to use them.
|
||||
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
|
||||
may be used.
|
||||
Tuple unpacking for variables is now treated as syntax sugar that directly
|
||||
expands into multiple assignments. Along with this, tuple unpacking for
|
||||
variables can now be nested.
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
|
||||
symbols in the `system` module:
|
||||
- Aliases with `Error` suffix to exception types that have a `Defect` suffix
|
||||
(see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
|
||||
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
|
||||
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
|
||||
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
|
||||
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
|
||||
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
|
||||
`DeadThreadError`, `NilAccessError`
|
||||
- `addQuitProc`, replaced by `exitprocs.addExitProc`
|
||||
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
|
||||
- `TaintedString`, formerly a distinct alias to `string`
|
||||
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
|
||||
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
|
||||
```nim
|
||||
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
|
||||
in the `system` module.
|
||||
# Now nesting is supported!
|
||||
let (x, (_, y), _, z) = returnsNestedTuple()
|
||||
|
||||
- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore.
|
||||
```
|
||||
|
||||
- The `gc:v2` option is removed.
|
||||
### Improved type inference
|
||||
|
||||
- The `mainmodule` and `m` options are removed.
|
||||
A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases.
|
||||
|
||||
- The `threads:on` option is now the default.
|
||||
For example, code like the following now compiles:
|
||||
|
||||
- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via
|
||||
`experimental:flexibleOptionalParams`.
|
||||
```nim
|
||||
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
|
||||
```
|
||||
|
||||
- Automatic dereferencing (experimental feature) is removed.
|
||||
### Forbidden Tags
|
||||
|
||||
- The `Math.trunc` polyfill for targeting Internet Explorer was
|
||||
previously included in most JavaScript output files.
|
||||
Now, it is only included with `-d:nimJsMathTruncPolyfill`.
|
||||
If you are targeting Internet Explorer, you may choose to enable this option
|
||||
or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
|
||||
Nim uses `Math.trunc` for the division and modulo operators for integers.
|
||||
[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition
|
||||
of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types.
|
||||
|
||||
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
|
||||
`sink` for optimization purposes.
|
||||
For example:
|
||||
|
||||
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises.
|
||||
```nim
|
||||
|
||||
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
|
||||
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
|
||||
```nim
|
||||
type Foo = distinct ptr int
|
||||
|
||||
# Before:
|
||||
var x: Foo = nil
|
||||
# After:
|
||||
var x: Foo = Foo(nil)
|
||||
```
|
||||
- Removed two type pragma syntaxes deprecated since 0.20, namely
|
||||
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`.
|
||||
|
||||
- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
|
||||
with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
|
||||
This decision was made with the assumption that the old syntax was used rarely;
|
||||
if your code used the old syntax, please be aware of this change.
|
||||
|
||||
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
|
||||
are no longer experimental.
|
||||
type IO = object ## input/output effect
|
||||
proc readLine(): string {.tags: [IO].} = discard
|
||||
proc echoLine(): void = discard
|
||||
|
||||
- Removed the `nimIncrSeqV3` define.
|
||||
proc no_IO_please() {.forbids: [IO].} =
|
||||
# this is OK because it didn't define any tag:
|
||||
echoLine()
|
||||
# the compiler prevents this:
|
||||
let y = readLine()
|
||||
|
||||
- `macros.getImpl` for `const` symbols now returns the full definition node
|
||||
(as `nnkConstDef`) rather than the AST of the constant value.
|
||||
```
|
||||
|
||||
- Lock levels are deprecated, now a noop.
|
||||
### New standard library modules
|
||||
|
||||
- ORC is now the default memory management strategy. Use
|
||||
`--mm:refc` for a transition period.
|
||||
|
||||
- `strictEffects` are no longer experimental.
|
||||
Use `legacy:laxEffects` to keep backward compatibility.
|
||||
|
||||
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
|
||||
if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`.
|
||||
|
||||
- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning.
|
||||
This warning will become an error in future versions! Use a `cast` operation
|
||||
like `cast[cstring](x)` instead.
|
||||
|
||||
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
|
||||
|
||||
- Redefining templates with the same signature was previously
|
||||
allowed to support certain macro code. To do this explicitly, the
|
||||
`{.redefine.}` pragma has been added. Note that this is only for templates.
|
||||
Implicit redefinition of templates is now deprecated and will give an error in the future.
|
||||
|
||||
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
|
||||
|
||||
- Several Standard libraries are moved to nimble packages, use `nimble` to install them:
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
|
||||
- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
|
||||
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
||||
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
||||
|
||||
- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered.
|
||||
|
||||
- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
||||
|
||||
- Underscores (`_`) as routine parameters are now ignored and cannot be used in the routine body.
|
||||
The following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
```
|
||||
|
||||
Instead, the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
```
|
||||
- Underscores (`_`) as generic parameters are not supported and cannot be used.
|
||||
Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else:
|
||||
|
||||
```nim
|
||||
proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile
|
||||
proc foo[T](t: typedesc[T]): string = "BAR" # Can compile
|
||||
```
|
||||
|
||||
- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
- OpenSSL 3 is now supported.
|
||||
- `macros.parseExpr` and `macros.parseStmt` now accept an optional
|
||||
filename argument for more informative errors.
|
||||
- Module `colors` expanded with missing colors from the CSS color standard.
|
||||
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
|
||||
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
|
||||
- The `md5` module now works at compile time and in JavaScript.
|
||||
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables.
|
||||
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
|
||||
making limiting it to just the first char (`last = 0`) valid.
|
||||
- `random.rand` now works with `Ordinal`s.
|
||||
- Undeprecated `os.isvalidfilename`.
|
||||
- `std/oids` now uses `int64` to store time internally (before it was int32).
|
||||
- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`.
|
||||
- `std/uri.Uri.isIpv6` is now exported.
|
||||
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
|
||||
|
||||
|
||||
- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`.
|
||||
- `std/jsfetch.newFetchOptions` now has default values for all parameters
|
||||
- `std/jsformdata` now accepts `Blob` data type.
|
||||
|
||||
- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433).
|
||||
|
||||
- New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall.
|
||||
|
||||
This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
|
||||
|
||||
On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
|
||||
|
||||
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
|
||||
```sh
|
||||
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
|
||||
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
|
||||
$ sh build.sh # per unix install instructions
|
||||
$ bin/nim c koch # per unix install instructions
|
||||
$ ./koch boot -d:release # per unix install instructions
|
||||
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
|
||||
```
|
||||
|
||||
This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular).
|
||||
|
||||
[//]: # "Additions:"
|
||||
- Added ISO 8601 week date utilities in `times`:
|
||||
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
|
||||
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
|
||||
- Added a `initDateTime` overload to create a datetime from an ISO week date.
|
||||
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
|
||||
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
|
||||
- Added new modules which were part of `std/os`:
|
||||
- Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling.
|
||||
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
|
||||
- Added `std/cmdline` for reading command line parameters.
|
||||
- Added `sep` parameter in `std/uri` to specify the query separator.
|
||||
- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
|
||||
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
|
||||
in `jscore` for JavaScript targets.
|
||||
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
|
||||
- Added `complex.sgn` for obtaining the phase of complex numbers.
|
||||
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
|
||||
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
|
||||
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
|
||||
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
|
||||
`toggleAttribute`, and `matches` to `std/dom`.
|
||||
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)
|
||||
- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460
|
||||
- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse.
|
||||
- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse.
|
||||
- Added `safe` parameter to `base64.encodeMime`.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
- Deprecated `selfExe` for Nimscript.
|
||||
- Deprecated `std/sums`.
|
||||
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
|
||||
Now only `byte` and `char` are supported.
|
||||
|
||||
[//]: # "Removals:"
|
||||
- Removed deprecated module `parseopt2`.
|
||||
- Removed deprecated module `sharedstrings`.
|
||||
- Removed deprecated module `dom_extensions`.
|
||||
- Removed deprecated module `LockFreeHash`.
|
||||
- Removed deprecated module `events`.
|
||||
- Removed deprecated `oids.oidToString`.
|
||||
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
|
||||
- Removed deprecated `jsre.test` and `jsre.toString`.
|
||||
- Removed deprecated `math.c_frexp`.
|
||||
- Removed deprecated `` httpcore.`==` ``.
|
||||
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types.
|
||||
- Removed deprecated `osproc.poDemon`, symbol with typo.
|
||||
- Removed deprecated `tables.rightSize`.
|
||||
|
||||
|
||||
- Removed deprecated `posix.CLONE_STOPPED`.
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma
|
||||
which can be used to disable certain effects in proc types.
|
||||
- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
|
||||
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
|
||||
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
|
||||
for the right-hand side of type definitions in type sections. Previously
|
||||
they would error with "invalid indentation".
|
||||
|
||||
- Compile-time define changes:
|
||||
- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
|
||||
In the command line, this is defined as `-d:a.b.c`. Older versions can
|
||||
use accents as in ``defined(`a.b.c`)`` to access such defines.
|
||||
- [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
|
||||
now support a string argument for qualified define names.
|
||||
|
||||
```nim
|
||||
# -d:package.FooBar=42
|
||||
const FooBar {.intdefine: "package.FooBar".}: int = 5
|
||||
echo FooBar # 42
|
||||
```
|
||||
|
||||
This was added to help disambiguate similar define names for different packages.
|
||||
In older versions, this could only be achieved with something like the following:
|
||||
|
||||
```nim
|
||||
const FooBar = block:
|
||||
const `package.FooBar` {.intdefine.}: int = 5
|
||||
`package.FooBar`
|
||||
```
|
||||
- A generic `define` pragma for constants has been added that interprets
|
||||
the value of the define based on the type of the constant value.
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-define-pragma)
|
||||
for a list of supported types.
|
||||
|
||||
- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
|
||||
- Templates now accept macro pragmas.
|
||||
- Macro pragmas for var/let/const sections have been redesigned in a way that works
|
||||
similarly to routine macro pragmas. The new behavior is documented in the
|
||||
[experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
|
||||
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
|
||||
allowing multiple type definitions to be injected in place of the original type definition.
|
||||
|
||||
```nim
|
||||
import macros
|
||||
macro multiply(amount: static int, s: untyped): untyped =
|
||||
let name = $s[0].basename
|
||||
result = newNimNode(nnkTypeSection)
|
||||
for i in 1 .. amount:
|
||||
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
|
||||
type
|
||||
Foo = object
|
||||
Bar {.multiply: 3.} = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
# becomes
|
||||
type
|
||||
Foo = object
|
||||
Bar1 = object
|
||||
x, y, z: int
|
||||
Bar2 = object
|
||||
x, y, z: int
|
||||
Bar3 = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
```
|
||||
|
||||
- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
|
||||
has been implemented for a variety of basic cases. For example, code like the following now compiles:
|
||||
|
||||
```nim
|
||||
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
|
||||
```
|
||||
|
||||
- `cstring` is now accepted as a selector in `case` statements, removing the
|
||||
need to convert to `string`. On the JS backend, this is translated directly
|
||||
to a `switch` statement.
|
||||
|
||||
- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
|
||||
- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
|
||||
|
||||
- IBM Z architecture and macOS m1 arm64 architecture are supported.
|
||||
|
||||
- `=wasMoved` can be overridden by users.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
|
||||
reality better. (Nim moved away from all techniques based on "tracing".)
|
||||
|
||||
- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
|
||||
|
||||
- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
|
||||
without requiring `-d:nimVersion140` which is now a noop.
|
||||
|
||||
- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
|
||||
|
||||
- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
|
||||
related functions produced on the backend. This prevents conflicts with other Nim
|
||||
static libraries.
|
||||
|
||||
- When compiling for Release the flag `-fno-math-errno` is used for GCC.
|
||||
The famous `os` module got an overhaul. Several of its features are available
|
||||
under a new interface that introduces a `Path` abstraction. A `Path` is
|
||||
a `distinct string`, which improves the type safety when dealing with paths, files
|
||||
and directories.
|
||||
|
||||
Use:
|
||||
|
||||
- `std/oserrors` for OS error reporting.
|
||||
- `std/envvars` for environment variables handling.
|
||||
- `std/paths` for path handling.
|
||||
- `std/dirs` for directory creation/deletion/traversal.
|
||||
- `std/files` for file existence checking, file deletions and moves.
|
||||
- `std/symlinks` for symlink handling.
|
||||
- `std/appdirs` for accessing configuration/home/temp directories.
|
||||
- `std/cmdline` for reading command line parameters.
|
||||
|
||||
### Consistent underscore handling
|
||||
|
||||
The underscore identifier (`_`) is now generally not added to scope when
|
||||
used as the name of a definition. While this was already the case for
|
||||
variables, it is now also the case for routine parameters, generic
|
||||
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
|
||||
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
|
||||
echo foo[int]()
|
||||
|
||||
proc _() = echo "_"
|
||||
_()
|
||||
|
||||
type _ = int
|
||||
let x: _ = 3
|
||||
```
|
||||
|
||||
Whereas the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
|
||||
proc foo[_, _](): int = 123
|
||||
echo foo[int, bool]()
|
||||
|
||||
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
|
||||
echo foo(int, bool)
|
||||
|
||||
proc _() = echo "one"
|
||||
proc _() = echo "two"
|
||||
|
||||
type _ = int
|
||||
type _ = float
|
||||
```
|
||||
|
||||
### JavaScript codegen improvement
|
||||
|
||||
The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
||||
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
|
||||
JS code generation, code using these types to interface with the JS backend
|
||||
may need to be updated. Note that `int` and `uint` are not affected.
|
||||
|
||||
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
|
||||
and in the case of potential bugs with the new implementation, the
|
||||
old behavior is currently still supported with the command line option
|
||||
`--jsbigint64:off`.
|
||||
|
||||
|
||||
## Docgen improvements
|
||||
|
||||
`Markdown` is now the default markup language of doc comments (instead
|
||||
of the legacy `RstMarkdown` mode). In this release we begin to separate
|
||||
RST and Markdown features to better follow specification of each
|
||||
language, with the focus on Markdown development.
|
||||
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
|
||||
|
||||
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
|
||||
select the markup language mode in the doc comments of the current `.nim`
|
||||
file for processing by `nim doc`:
|
||||
|
||||
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
|
||||
some Pandoc Markdown features + some RST features that are missing
|
||||
in our current implementation of CommonMark and Pandoc Markdown.
|
||||
2. `RST` closely follows the RST spec with few additional Nim features.
|
||||
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
|
||||
is kept for the sake of compatibility and ease of migration.
|
||||
|
||||
* Added separate `md2html` and `rst2html` commands for processing
|
||||
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
|
||||
|
||||
* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
|
||||
* Docgen now supports concise syntax for referencing Nim symbols:
|
||||
instead of specifying HTML anchors directly one can use original
|
||||
Nim symbol declarations (adding the aforementioned link brackets
|
||||
`[...]` around them).
|
||||
* To use this feature across modules, a new `importdoc` directive was added.
|
||||
Using this feature for referencing also helps to ensure that links
|
||||
(inside one module or the whole project) are not broken.
|
||||
* Added support for RST & Markdown quote blocks (blocks starting with `>`).
|
||||
* Added a popular Markdown definition lists extension.
|
||||
* Added Markdown indented code blocks (blocks indented by >= 4 spaces).
|
||||
* Added syntax for additional parameters to Markdown code blocks:
|
||||
|
||||
```nim test="nim c $1"
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## C++ interop enhancements
|
||||
|
||||
Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
|
||||
Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize
|
||||
the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types.
|
||||
|
||||
It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```cpp
|
||||
|
||||
struct Base {
|
||||
int someValue;
|
||||
Base(int inValue) {
|
||||
someValue = inValue;
|
||||
};
|
||||
};
|
||||
|
||||
class IPrinter {
|
||||
public:
|
||||
virtual void print() = 0;
|
||||
};
|
||||
```
|
||||
|
||||
```nim
|
||||
|
||||
type
|
||||
Base* {.importcpp, inheritable.} = object
|
||||
someValue*: int32
|
||||
IPrinter* {.importcpp.} = object
|
||||
|
||||
const objTemplate = """
|
||||
struct $1 : public $3, public IPrinter {
|
||||
$2
|
||||
};
|
||||
""";
|
||||
|
||||
type NimChild {.codegenDecl: objTemplate .} = object of Base
|
||||
|
||||
proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} =
|
||||
echo "It calls the base constructor passing " & $this.someValue
|
||||
this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`.
|
||||
|
||||
proc print*(self: NimChild) {.virtual.} =
|
||||
echo "Some value is " & $self.someValue
|
||||
|
||||
let child = makeNimChild(10)
|
||||
child.print()
|
||||
```
|
||||
|
||||
It outputs:
|
||||
|
||||
```
|
||||
It calls the base constructor passing 10
|
||||
Some value is 20
|
||||
```
|
||||
|
||||
|
||||
## ARC/ORC refinements
|
||||
|
||||
With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete:
|
||||
|
||||
1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable.
|
||||
2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations.
|
||||
3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter.
|
||||
|
||||
With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what?
|
||||
Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference.
|
||||
|
||||
But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of
|
||||
providing a stable ABI it is important not to lose any efficiency in the calling conventions.
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`).
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
|
||||
- nimgrep now offers the option `--inContext` (and `--notInContext`), which
|
||||
allows to filter only matches with the context block containing a given pattern.
|
||||
- nimgrep: names of options containing "include/exclude" are deprecated,
|
||||
e.g. instead of `--includeFile` and `--excludeFile` we have
|
||||
`--filename` and `--notFilename` respectively.
|
||||
Also, the semantics are now consistent for such positive/negative filters.
|
||||
- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions.
|
||||
|
||||
|
||||
## Porting guide
|
||||
|
||||
### Block and Break
|
||||
|
||||
Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn:
|
||||
|
||||
```nim
|
||||
|
||||
block:
|
||||
a()
|
||||
if cond:
|
||||
break
|
||||
b()
|
||||
|
||||
```
|
||||
|
||||
Into:
|
||||
|
||||
```nim
|
||||
|
||||
block maybePerformB:
|
||||
a()
|
||||
if cond:
|
||||
break maybePerformB
|
||||
b()
|
||||
|
||||
```
|
||||
|
||||
### Strict funcs
|
||||
|
||||
The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type Node = ref object
|
||||
s: string
|
||||
|
||||
func create(s: string): Node =
|
||||
result = Node()
|
||||
result.s = s # store to result[]
|
||||
|
||||
```
|
||||
|
||||
Now it has to be rewritten to:
|
||||
|
||||
```nim
|
||||
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type Node = ref object
|
||||
s: string
|
||||
|
||||
func create(s: string): Node =
|
||||
result = Node(s: s)
|
||||
|
||||
```
|
||||
|
||||
### Standard library
|
||||
|
||||
Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them:
|
||||
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
- `std/md5` => `checksums/md5`
|
||||
- `std/sha1` => `checksums/sha1`
|
||||
- `std/sums` => `sums`
|
||||
|
||||
560
changelogs/changelog_2_0_0_details.md
Normal file
560
changelogs/changelog_2_0_0_details.md
Normal file
@@ -0,0 +1,560 @@
|
||||
# v2.0.0 - 2023-08-01
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- ORC is now the default memory management strategy. Use
|
||||
`--mm:refc` for a transition period.
|
||||
|
||||
- The `threads:on` option is now the default.
|
||||
|
||||
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`.
|
||||
|
||||
- `addr` is now available for all addressable locations,
|
||||
`unsafeAddr` is now deprecated and an alias for `addr`.
|
||||
|
||||
- Certain definitions from the default `system` module have been moved to
|
||||
the following new modules:
|
||||
|
||||
- `std/syncio`
|
||||
- `std/assertions`
|
||||
- `std/formatfloat`
|
||||
- `std/objectdollar`
|
||||
- `std/widestrs`
|
||||
- `std/typedthreads`
|
||||
- `std/sysatomics`
|
||||
|
||||
In the future, these definitions will be removed from the `system` module,
|
||||
and their respective modules will have to be imported to use them.
|
||||
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
|
||||
may be used.
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
|
||||
symbols in the `system` module:
|
||||
- Aliases with an `Error` suffix to exception types that have a `Defect` suffix
|
||||
(see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
|
||||
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
|
||||
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
|
||||
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
|
||||
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
|
||||
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
|
||||
`DeadThreadError`, `NilAccessError`
|
||||
- `addQuitProc`, replaced by `exitprocs.addExitProc`
|
||||
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
|
||||
- `TaintedString`, formerly a distinct alias to `string`
|
||||
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
|
||||
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
|
||||
in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`.
|
||||
|
||||
- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore.
|
||||
|
||||
- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to
|
||||
`pointer` anymore. `cast` may be used instead.
|
||||
|
||||
- The `gc:v2` option is removed.
|
||||
|
||||
- The `mainmodule` and `m` options are removed.
|
||||
|
||||
- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405))
|
||||
are now opt-in via `experimental:flexibleOptionalParams`.
|
||||
|
||||
- Automatic dereferencing (experimental feature) is removed.
|
||||
|
||||
- The `Math.trunc` polyfill for targeting Internet Explorer was
|
||||
previously included in most JavaScript output files.
|
||||
Now, it is only included with `-d:nimJsMathTruncPolyfill`.
|
||||
If you are targeting Internet Explorer, you may choose to enable this option
|
||||
or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
|
||||
Nim uses `Math.trunc` for the division and modulo operators for integers.
|
||||
|
||||
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
|
||||
`sink` for optimization purposes.
|
||||
|
||||
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
|
||||
|
||||
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
|
||||
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
|
||||
```nim
|
||||
type Foo = distinct ptr int
|
||||
|
||||
# Before:
|
||||
var x: Foo = nil
|
||||
# After:
|
||||
var x: Foo = Foo(nil)
|
||||
```
|
||||
- Removed two type pragma syntaxes deprecated since 0.20, namely
|
||||
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead,
|
||||
use `type Foo[T] {.final.} = object`.
|
||||
|
||||
- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
|
||||
with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
|
||||
This decision was made with the assumption that the old syntax was used rarely;
|
||||
if your code used the old syntax, please be aware of this change.
|
||||
|
||||
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
|
||||
are no longer experimental.
|
||||
|
||||
- `macros.getImpl` for `const` symbols now returns the full definition node
|
||||
(as `nnkConstDef`) rather than the AST of the constant value.
|
||||
|
||||
- Lock levels are deprecated, now a noop.
|
||||
|
||||
- `strictEffects` are no longer experimental.
|
||||
Use `legacy:laxEffects` to keep backward compatibility.
|
||||
|
||||
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
|
||||
if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`.
|
||||
|
||||
- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning.
|
||||
This warning will become an error in future versions! Use a `cast` operation
|
||||
like `cast[cstring](x)` instead.
|
||||
|
||||
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
|
||||
|
||||
- Redefining templates with the same signature was previously
|
||||
allowed to support certain macro code. To do this explicitly, the
|
||||
`{.redefine.}` pragma has been added. Note that this is only for templates.
|
||||
Implicit redefinition of templates is now deprecated and will give an error in the future.
|
||||
|
||||
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
|
||||
|
||||
- Several Standard libraries have been moved to nimble packages, use `nimble` to install them:
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
- `std/md5` => `checksums/md5`
|
||||
- `std/sha1` => `checksums/sha1`
|
||||
- `std/sums` => `std/sums`
|
||||
|
||||
- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
|
||||
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
||||
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
||||
|
||||
- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning:
|
||||
```nim
|
||||
try:
|
||||
discard
|
||||
except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept]
|
||||
discard
|
||||
```
|
||||
|
||||
- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
||||
|
||||
- The underscore identifier (`_`) is now generally not added to scope when
|
||||
used as the name of a definition. While this was already the case for
|
||||
variables, it is now also the case for routine parameters, generic
|
||||
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
|
||||
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
|
||||
echo foo[int]()
|
||||
|
||||
proc _() = echo "_"
|
||||
_()
|
||||
|
||||
type _ = int
|
||||
let x: _ = 3
|
||||
```
|
||||
|
||||
Whereas the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
|
||||
proc foo[_, _](): int = 123
|
||||
echo foo[int, bool]()
|
||||
|
||||
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
|
||||
echo foo(int, bool)
|
||||
|
||||
proc _() = echo "one"
|
||||
proc _() = echo "two"
|
||||
|
||||
type _ = int
|
||||
type _ = float
|
||||
```
|
||||
|
||||
- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
||||
|
||||
- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
||||
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
|
||||
JS code generation, code using these types to interface with the JS backend
|
||||
may need to be updated. Note that `int` and `uint` are not affected.
|
||||
|
||||
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
|
||||
and in the case of potential bugs with the new implementation, the
|
||||
old behavior is currently still supported with the command line option
|
||||
`--jsbigint64:off`.
|
||||
|
||||
- The `proc` and `iterator` type classes now respectively only match
|
||||
procs and iterators. Previously both type classes matched any of
|
||||
procs or iterators.
|
||||
|
||||
```nim
|
||||
proc prc(): int =
|
||||
123
|
||||
|
||||
iterator iter(): int =
|
||||
yield 123
|
||||
|
||||
proc takesProc[T: proc](x: T) = discard
|
||||
proc takesIter[T: iterator](x: T) = discard
|
||||
|
||||
# always compiled:
|
||||
takesProc(prc)
|
||||
takesIter(iter)
|
||||
# no longer compiles:
|
||||
takesProc(iter)
|
||||
takesIter(prc)
|
||||
```
|
||||
|
||||
- The `proc` and `iterator` type classes now accept a calling convention pragma
|
||||
(i.e. `proc {.closure.}`) that must be shared by matching proc or iterator
|
||||
types. Previously, pragmas were parsed but discarded if no parameter list
|
||||
was given.
|
||||
|
||||
This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with
|
||||
an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma
|
||||
node in the same place as in a concrete `proc` or `iterator` type node. This
|
||||
state of the AST may be unexpected to existing code, both due to the
|
||||
replacement of the `nnkFormalParams` node as well as having child nodes
|
||||
unlike other type class AST.
|
||||
|
||||
- Signed integer literals in `set` literals now default to a range type of
|
||||
`0..255` instead of `0..65535` (the maximum size of sets).
|
||||
|
||||
- `case` statements with `else` branches put before `elif`/`of` branches in macros
|
||||
are rejected with "invalid order of case branches".
|
||||
|
||||
- Destructors now default to `.raises: []` (i.e. destructors must not raise
|
||||
unlisted exceptions) and explicitly raising destructors are implementation
|
||||
defined behavior.
|
||||
|
||||
- The very old, undocumented `deprecated` pragma statement syntax for
|
||||
deprecated aliases is now a no-op. The regular deprecated pragma syntax is
|
||||
generally sufficient instead.
|
||||
|
||||
```nim
|
||||
# now does nothing:
|
||||
{.deprecated: [OldName: NewName].}
|
||||
|
||||
# instead use:
|
||||
type OldName* {.deprecated: "use NewName instead".} = NewName
|
||||
const oldName* {.deprecated: "use newName instead".} = newName
|
||||
```
|
||||
|
||||
`defined(nimalias)` can be used to check for versions when this syntax was
|
||||
available; however since code that used this syntax is usually very old,
|
||||
these deprecated aliases are likely not used anymore and it may make sense
|
||||
to simply remove these statements.
|
||||
|
||||
- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer
|
||||
declared when they are not available on the backend. Previously it would call
|
||||
`doAssert false` at runtime despite the condition being checkable at compile-time.
|
||||
|
||||
- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated.
|
||||
|
||||
- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
- OpenSSL 3 is now supported.
|
||||
- `macros.parseExpr` and `macros.parseStmt` now accept an optional
|
||||
`filename` argument for more informative errors.
|
||||
- The `colors` module is expanded with missing colors from the CSS color standard.
|
||||
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
|
||||
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
|
||||
- The `md5` module now works at compile time and in JavaScript.
|
||||
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables.
|
||||
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
|
||||
making limiting it to just the first char (`last = 0`) valid.
|
||||
- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator.
|
||||
- `random.rand` now works with `Ordinal`s.
|
||||
- Undeprecated `os.isvalidfilename`.
|
||||
- `std/oids` now uses `int64` to store time internally (before, it was int32).
|
||||
- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`.
|
||||
- `std/uri.Uri.isIpv6` is now exported.
|
||||
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
|
||||
|
||||
|
||||
- `std/jsfetch.newFetchOptions` now has default values for all parameters.
|
||||
- `std/jsformdata` now accepts the `Blob` data type.
|
||||
|
||||
- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433).
|
||||
|
||||
- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall.
|
||||
|
||||
This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
|
||||
|
||||
On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
|
||||
|
||||
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
|
||||
```sh
|
||||
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
|
||||
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
|
||||
$ sh build.sh # per unix install instructions
|
||||
$ bin/nim c koch # per unix install instructions
|
||||
$ ./koch boot -d:release # per unix install instructions
|
||||
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
|
||||
```
|
||||
|
||||
This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular).
|
||||
|
||||
[//]: # "Additions:"
|
||||
- Added ISO 8601 week date utilities in `times`:
|
||||
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
|
||||
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
|
||||
- Added an `initDateTime` overload to create a `DateTime` from an ISO week date.
|
||||
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
|
||||
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
|
||||
- Added new modules which were previously part of `std/os`:
|
||||
- Added `std/oserrors` for OS error reporting.
|
||||
- Added `std/envvars` for environment variables handling.
|
||||
- Added `std/cmdline` for reading command line parameters.
|
||||
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
|
||||
- Added `sep` parameter in `std/uri` to specify the query separator.
|
||||
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
|
||||
- Added `complex.sgn` for obtaining the phase of complex numbers.
|
||||
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
|
||||
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
|
||||
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
|
||||
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
|
||||
`toggleAttribute`, and `matches` to `std/dom`.
|
||||
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices).
|
||||
- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460).
|
||||
- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse.
|
||||
- Added a `safe` parameter to `base64.encodeMime`.
|
||||
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
|
||||
- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences.
|
||||
- `std/jscore` for the JavaScript target:
|
||||
+ Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
|
||||
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask).
|
||||
+ Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`.
|
||||
- Added `BackwardsIndex` overload for `CacheSeq`.
|
||||
- Added support for nested `with` blocks in `std/with`.
|
||||
- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time.
|
||||
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
- Deprecated `selfExe` for Nimscript.
|
||||
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
|
||||
Now only `byte` and `char` are supported.
|
||||
|
||||
[//]: # "Removals:"
|
||||
- Removed deprecated module `parseopt2`.
|
||||
- Removed deprecated module `sharedstrings`.
|
||||
- Removed deprecated module `dom_extensions`.
|
||||
- Removed deprecated module `LockFreeHash`.
|
||||
- Removed deprecated module `events`.
|
||||
- Removed deprecated `oids.oidToString`.
|
||||
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
|
||||
- Removed deprecated `jsre.test` and `jsre.toString`.
|
||||
- Removed deprecated `math.c_frexp`.
|
||||
- Removed deprecated `` httpcore.`==` ``.
|
||||
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types.
|
||||
- Removed deprecated `osproc.poDemon`, symbol with typo.
|
||||
- Removed deprecated `tables.rightSize`.
|
||||
|
||||
|
||||
- Removed deprecated `posix.CLONE_STOPPED`.
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma
|
||||
which can be used to disable certain effects in proc types.
|
||||
- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
|
||||
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
|
||||
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
|
||||
for the right-hand side of type definitions in type sections. Previously
|
||||
they would error with "invalid indentation".
|
||||
|
||||
- Compile-time define changes:
|
||||
- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
|
||||
In the command line, this is defined as `-d:a.b.c`. Older versions can
|
||||
use backticks as in ``defined(`a.b.c`)`` to access such defines.
|
||||
- [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
|
||||
now support a string argument for qualified define names.
|
||||
|
||||
```nim
|
||||
# -d:package.FooBar=42
|
||||
const FooBar {.intdefine: "package.FooBar".}: int = 5
|
||||
echo FooBar # 42
|
||||
```
|
||||
|
||||
This was added to help disambiguate similar define names for different packages.
|
||||
In older versions, this could only be achieved with something like the following:
|
||||
|
||||
```nim
|
||||
const FooBar = block:
|
||||
const `package.FooBar` {.intdefine.}: int = 5
|
||||
`package.FooBar`
|
||||
```
|
||||
- A generic `define` pragma for constants has been added that interprets
|
||||
the value of the define based on the type of the constant value.
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma)
|
||||
for a list of supported types.
|
||||
|
||||
- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
|
||||
- Templates now accept macro pragmas.
|
||||
- Macro pragmas for var/let/const sections have been redesigned in a way that works
|
||||
similarly to routine macro pragmas. The new behavior is documented in the
|
||||
[experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
|
||||
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
|
||||
allowing multiple type definitions to be injected in place of the original type definition.
|
||||
|
||||
```nim
|
||||
import macros
|
||||
|
||||
macro multiply(amount: static int, s: untyped): untyped =
|
||||
let name = $s[0].basename
|
||||
result = newNimNode(nnkTypeSection)
|
||||
for i in 1 .. amount:
|
||||
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
|
||||
|
||||
type
|
||||
Foo = object
|
||||
Bar {.multiply: 3.} = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
# becomes
|
||||
type
|
||||
Foo = object
|
||||
Bar1 = object
|
||||
x, y, z: int
|
||||
Bar2 = object
|
||||
x, y, z: int
|
||||
Bar3 = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
```
|
||||
|
||||
- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
|
||||
has been implemented for a variety of basic cases. For example, code like the following now compiles:
|
||||
|
||||
```nim
|
||||
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
|
||||
```
|
||||
|
||||
- `cstring` is now accepted as a selector in `case` statements, removing the
|
||||
need to convert to `string`. On the JS backend, this is translated directly
|
||||
to a `switch` statement.
|
||||
|
||||
- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
|
||||
- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
|
||||
|
||||
- IBM Z architecture and macOS m1 arm64 architecture are supported.
|
||||
|
||||
- `=wasMoved` can now be overridden by users.
|
||||
|
||||
- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code
|
||||
generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care.
|
||||
|
||||
- Tuple unpacking for variables is now treated as syntax sugar that directly
|
||||
expands into multiple assignments. Along with this, tuple unpacking for
|
||||
variables can now be nested.
|
||||
|
||||
```nim
|
||||
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
|
||||
|
||||
let (x, (_, y), _, z) = returnsNestedTuple()
|
||||
# roughly becomes
|
||||
let
|
||||
tmpTup1 = returnsNestedTuple()
|
||||
x = tmpTup1[0]
|
||||
tmpTup2 = tmpTup1[1]
|
||||
y = tmpTup2[1]
|
||||
z = tmpTup1[3]
|
||||
```
|
||||
|
||||
As a result `nnkVarTuple` nodes in variable sections will no longer be
|
||||
reflected in `typed` AST.
|
||||
|
||||
- C++ interoperability:
|
||||
- New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added.
|
||||
- Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
|
||||
reality better. (Nim moved away from all techniques based on "tracing".)
|
||||
|
||||
- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
|
||||
|
||||
- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
|
||||
without requiring `-d:nimVersion140` which is now a noop.
|
||||
|
||||
- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
|
||||
|
||||
- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
|
||||
related functions produced on the backend. This prevents conflicts with other Nim
|
||||
static libraries.
|
||||
|
||||
- When compiling for release, the flag `-fno-math-errno` is used for GCC.
|
||||
- Removed deprecated `LineTooLong` hint.
|
||||
- Line numbers and file names of source files work correctly inside templates for JavaScript targets.
|
||||
|
||||
- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers.
|
||||
|
||||
|
||||
## Docgen
|
||||
|
||||
- `Markdown` is now the default markup language of doc comments (instead
|
||||
of the legacy `RstMarkdown` mode). In this release we begin to separate
|
||||
RST and Markdown features to better follow specification of each
|
||||
language, with the focus on Markdown development.
|
||||
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
|
||||
|
||||
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
|
||||
select the markup language mode in the doc comments of the current `.nim`
|
||||
file for processing by `nim doc`:
|
||||
|
||||
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
|
||||
some Pandoc Markdown features + some RST features that are missing
|
||||
in our current implementation of CommonMark and Pandoc Markdown.
|
||||
2. `RST` closely follows the RST spec with few additional Nim features.
|
||||
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
|
||||
is kept for the sake of compatibility and ease of migration.
|
||||
|
||||
* Added separate `md2html` and `rst2html` commands for processing
|
||||
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
|
||||
|
||||
- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
|
||||
- Docgen now supports concise syntax for referencing Nim symbols:
|
||||
instead of specifying HTML anchors directly one can use original
|
||||
Nim symbol declarations (adding the aforementioned link brackets
|
||||
`[...]` around them).
|
||||
* To use this feature across modules, a new `importdoc` directive was added.
|
||||
Using this feature for referencing also helps to ensure that links
|
||||
(inside one module or the whole project) are not broken.
|
||||
- Added support for RST & Markdown quote blocks (blocks starting with `>`).
|
||||
- Added a popular Markdown definition lists extension.
|
||||
- Added Markdown indented code blocks (blocks indented by >= 4 spaces).
|
||||
- Added syntax for additional parameters to Markdown code blocks:
|
||||
|
||||
```nim test="nim c $1"
|
||||
...
|
||||
```
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
|
||||
- nimgrep added the option `--inContext` (and `--notInContext`), which
|
||||
allows to filter only matches with the context block containing a given pattern.
|
||||
- nimgrep: names of options containing "include/exclude" are deprecated,
|
||||
e.g. instead of `--includeFile` and `--excludeFile` we have
|
||||
`--filename` and `--notFilename` respectively.
|
||||
Also the semantics are now consistent for such positive/negative filters.
|
||||
- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice.
|
||||
@@ -58,7 +58,11 @@ _nimNumCpu(){
|
||||
# FreeBSD | macOS: $(sysctl -n hw.ncpu)
|
||||
# OpenBSD: $(sysctl -n hw.ncpuonline)
|
||||
# windows: $NUMBER_OF_PROCESSORS ?
|
||||
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
|
||||
if env | grep -q '^NIMCORES='; then
|
||||
echo $NIMCORES
|
||||
else
|
||||
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
|
||||
fi
|
||||
}
|
||||
|
||||
_nimBuildCsourcesIfNeeded(){
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# abstract syntax tree + symbol table
|
||||
|
||||
import
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables, wordrecg
|
||||
from strutils import toLowerAscii
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
@@ -231,7 +231,7 @@ type
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 49 flags!
|
||||
TSymFlag* = enum # 51 flags!
|
||||
sfUsed, # read access of sym (for warnings) or simply used
|
||||
sfExported, # symbol is exported from module
|
||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||
@@ -283,7 +283,7 @@ type
|
||||
sfNamedParamCall, # symbol needs named parameter call syntax in target
|
||||
# language; for interfacing with Objective C
|
||||
sfDiscardable, # returned value may be discarded implicitly
|
||||
sfOverriden, # proc is overridden
|
||||
sfOverridden, # proc is overridden
|
||||
sfCallsite # A flag for template symbols to tell the
|
||||
# compiler it should use line information from
|
||||
# the calling side of the macro, not from the
|
||||
@@ -303,6 +303,8 @@ type
|
||||
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
||||
sfSingleUsedTemp # For temporaries that we know will only be used once
|
||||
sfNoalias # 'noalias' annotation, means C's 'restrict'
|
||||
# for templates and macros, means cannot be called
|
||||
# as a lone symbol (cannot use alias syntax)
|
||||
sfEffectsDelayed # an 'effectsDelayed' parameter
|
||||
sfGeneratedType # A anonymous generic type that is generated by the compiler for
|
||||
# objects that do not have generic parameters in case one of the
|
||||
@@ -310,6 +312,9 @@ type
|
||||
#
|
||||
# This is disallowed but can cause the typechecking to go into
|
||||
# an infinite loop, this flag is used as a sentinel to stop it.
|
||||
sfVirtual # proc is a C++ virtual function
|
||||
sfByCopy # param is marked as pass bycopy
|
||||
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
@@ -335,8 +340,8 @@ const
|
||||
|
||||
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
||||
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
||||
sfExperimental* = sfOverriden # module uses the .experimental switch
|
||||
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||
sfExperimental* = sfOverridden # module uses the .experimental switch
|
||||
sfGoto* = sfOverridden # var is used for 'goto' code generation
|
||||
sfWrittenTo* = sfBorrow # param is assigned to
|
||||
# currently unimplemented
|
||||
sfBase* = sfDiscriminant
|
||||
@@ -511,9 +516,10 @@ type
|
||||
nfFirstWrite # this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
nfOpenSym # node is a captured sym but can be overriden by local symbols
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46)
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
|
||||
tfVarargs, # procedure has C styled varargs
|
||||
# tyArray type represeting a varargs list
|
||||
tfNoSideEffect, # procedure type does not allow side effects
|
||||
@@ -583,6 +589,8 @@ type
|
||||
tfIsConstructor
|
||||
tfEffectSystemWorkaround
|
||||
tfIsOutParam
|
||||
tfSendable
|
||||
tfImplicitStatic
|
||||
|
||||
TTypeFlags* = set[TTypeFlag]
|
||||
|
||||
@@ -618,13 +626,12 @@ type
|
||||
# file (it is loaded on demand, which may
|
||||
# mean: never)
|
||||
skPackage, # symbol is a package (used for canonicalization)
|
||||
skAlias # an alias (needs to be resolved immediately)
|
||||
TSymKinds* = set[TSymKind]
|
||||
|
||||
const
|
||||
routineKinds* = {skProc, skFunc, skMethod, skIterator,
|
||||
skConverter, skMacro, skTemplate}
|
||||
ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds
|
||||
ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub} + routineKinds
|
||||
|
||||
tfUnion* = tfNoSideEffect
|
||||
tfGcSafe* = tfThread
|
||||
@@ -685,7 +692,7 @@ type
|
||||
mIsPartOf, mAstToStr, mParallel,
|
||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
mMove, mWasMoved, mDestroy, mTrace,
|
||||
mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace,
|
||||
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||
@@ -818,9 +825,6 @@ type
|
||||
locOther # location is something other
|
||||
TLocFlag* = enum
|
||||
lfIndirect, # backend introduced a pointer
|
||||
lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates
|
||||
# that the symbol has been imported via 'importc: "fullname"' and
|
||||
# no format string.
|
||||
lfNoDeepCopy, # no need for a deep copy
|
||||
lfNoDecl, # do not declare it in C
|
||||
lfDynamicLib, # link symbol to dynamic library
|
||||
@@ -855,7 +859,7 @@ type
|
||||
# keep in sync with PackedLib
|
||||
kind*: TLibKind
|
||||
generated*: bool # needed for the backends:
|
||||
isOverriden*: bool
|
||||
isOverridden*: bool
|
||||
name*: Rope
|
||||
path*: PNode # can be a string literal!
|
||||
|
||||
@@ -896,6 +900,7 @@ type
|
||||
info*: TLineInfo
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
|
||||
owner*: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
@@ -917,7 +922,9 @@ type
|
||||
# for modules, an unique index corresponding
|
||||
# to the module's fileIdx
|
||||
# for variables a slot index for the evaluator
|
||||
offset*: int # offset of record field
|
||||
offset*: int32 # offset of record field
|
||||
disamb*: int32 # disambiguation number; the basic idea is that
|
||||
# `<procname>__<module>_<disamb>`
|
||||
loc*: TLoc
|
||||
annex*: PLib # additional fields (seldom used, so we use a
|
||||
# reference to another object to save space)
|
||||
@@ -925,7 +932,7 @@ type
|
||||
cname*: string # resolved C declaration name in importc decl, e.g.:
|
||||
# proc fun() {.importc: "$1aux".} => cname = funaux
|
||||
constraint*: PNode # additional constraints like 'lit|result'; also
|
||||
# misused for the codegenDecl pragma in the hope
|
||||
# misused for the codegenDecl and virtual pragmas in the hope
|
||||
# it won't cause problems
|
||||
# for skModule the string literal to output for
|
||||
# deprecated modules.
|
||||
@@ -938,6 +945,7 @@ type
|
||||
attachedWasMoved,
|
||||
attachedDestructor,
|
||||
attachedAsgn,
|
||||
attachedDup,
|
||||
attachedSink,
|
||||
attachedTrace,
|
||||
attachedDeepCopy
|
||||
@@ -1081,7 +1089,8 @@ const
|
||||
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking}
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfOpenSym}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -1121,11 +1130,10 @@ const
|
||||
|
||||
proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`.
|
||||
# xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice}
|
||||
# which may simplify code.
|
||||
case a.kind
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
else: nil
|
||||
|
||||
const
|
||||
@@ -1141,13 +1149,17 @@ type
|
||||
symId*: int32
|
||||
typeId*: int32
|
||||
sealed*: bool
|
||||
disambTable*: CountTable[PIdent]
|
||||
|
||||
const
|
||||
PackageModuleId* = -3'i32
|
||||
|
||||
proc idGeneratorFromModule*(m: PSym): IdGenerator =
|
||||
assert m.kind == skModule
|
||||
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]())
|
||||
|
||||
proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator =
|
||||
result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]())
|
||||
|
||||
proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
|
||||
assert(not x.sealed)
|
||||
@@ -1339,11 +1351,15 @@ when false:
|
||||
echo k
|
||||
echo v
|
||||
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
|
||||
info: TLineInfo; options: TOptions = {}): PSym =
|
||||
# generates a symbol and initializes the hash field too
|
||||
assert not name.isNil
|
||||
let id = nextSymId idgen
|
||||
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
||||
options: options, owner: owner, offset: defaultOffset)
|
||||
options: options, owner: owner, offset: defaultOffset,
|
||||
disamb: getOrDefault(idgen.disambTable, name).int32)
|
||||
idgen.disambTable.inc name
|
||||
when false:
|
||||
if id.module == 48 and id.item == 39:
|
||||
writeStackTrace()
|
||||
@@ -1503,7 +1519,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
||||
|
||||
const
|
||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||
"=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
|
||||
"=wasMoved", "=destroy", "=copy", "=dup", "=sink", "=trace", "=deepcopy"]
|
||||
|
||||
proc `$`*(s: PSym): string =
|
||||
if s != nil:
|
||||
@@ -1557,8 +1573,8 @@ proc copyType*(t: PType, id: ItemId, owner: PSym): PType =
|
||||
proc exactReplica*(t: PType): PType =
|
||||
result = copyType(t, t.itemId, t.owner)
|
||||
|
||||
proc copySym*(s: PSym; id: ItemId): PSym =
|
||||
result = newSym(s.kind, s.name, id, s.owner, s.info, s.options)
|
||||
proc copySym*(s: PSym; idgen: IdGenerator): PSym =
|
||||
result = newSym(s.kind, s.name, idgen, s.owner, s.info, s.options)
|
||||
#result.ast = nil # BUGFIX; was: s.ast which made problems
|
||||
result.typ = s.typ
|
||||
result.flags = s.flags
|
||||
@@ -1573,9 +1589,9 @@ proc copySym*(s: PSym; id: ItemId): PSym =
|
||||
result.bitsize = s.bitsize
|
||||
result.alignment = s.alignment
|
||||
|
||||
proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
|
||||
proc createModuleAlias*(s: PSym, idgen: IdGenerator, newIdent: PIdent, info: TLineInfo;
|
||||
options: TOptions): PSym =
|
||||
result = newSym(s.kind, newIdent, id, s.owner, info, options)
|
||||
result = newSym(s.kind, newIdent, idgen, s.owner, info, options)
|
||||
# keep ID!
|
||||
result.ast = s.ast
|
||||
#result.id = s.id # XXX figure out what to do with the ID.
|
||||
@@ -1915,20 +1931,6 @@ proc isCompileTimeProc*(s: PSym): bool {.inline.} =
|
||||
result = s.kind == skMacro or
|
||||
s.kind in {skProc, skFunc} and sfCompileTime in s.flags
|
||||
|
||||
proc isRunnableExamples*(n: PNode): bool =
|
||||
# Templates and generics don't perform symbol lookups.
|
||||
result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
|
||||
n.kind == nkIdent and n.ident.s == "runnableExamples"
|
||||
|
||||
proc requiredParams*(s: PSym): int =
|
||||
# Returns the number of required params (without default values)
|
||||
# XXX: Perhaps we can store this in the `offset` field of the
|
||||
# symbol instead?
|
||||
for i in 1..<s.typ.len:
|
||||
if s.typ.n[i].sym.ast != nil:
|
||||
return i - 1
|
||||
return s.typ.len - 1
|
||||
|
||||
proc hasPattern*(s: PSym): bool {.inline.} =
|
||||
result = isRoutine(s) and s.ast[patternPos].kind != nkEmpty
|
||||
|
||||
@@ -2002,7 +2004,7 @@ proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
|
||||
# automatic dereferencing is deep, refs #18298.
|
||||
else: break
|
||||
assert result.sym != nil
|
||||
# result does not have to be object type
|
||||
|
||||
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||
assert t != nil
|
||||
@@ -2016,7 +2018,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||
result = true
|
||||
|
||||
proc isInfixAs*(n: PNode): bool =
|
||||
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
|
||||
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
|
||||
|
||||
proc skipColon*(n: PNode): PNode =
|
||||
result = n
|
||||
@@ -2024,10 +2026,12 @@ proc skipColon*(n: PNode): PNode =
|
||||
result = n[1]
|
||||
|
||||
proc findUnresolvedStatic*(n: PNode): PNode =
|
||||
# n.typ == nil: see issue #14802
|
||||
if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
|
||||
return n
|
||||
|
||||
if n.typ != nil and n.typ.kind == tyTypeDesc:
|
||||
let t = skipTypes(n.typ, {tyTypeDesc})
|
||||
if t.kind == tyGenericParam and t.len == 0:
|
||||
return n
|
||||
for son in n:
|
||||
let n = son.findUnresolvedStatic
|
||||
if n != nil: return n
|
||||
@@ -2065,6 +2069,12 @@ proc isClosureIterator*(typ: PType): bool {.inline.} =
|
||||
proc isClosure*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and typ.callConv == ccClosure
|
||||
|
||||
proc isNimcall*(s: PSym): bool {.inline.} =
|
||||
s.typ.callConv == ccNimCall
|
||||
|
||||
proc isExplicitCallConv*(s: PSym): bool {.inline.} =
|
||||
tfExplicitCallConv in s.typ.flags
|
||||
|
||||
proc isSinkParam*(s: PSym): bool {.inline.} =
|
||||
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " [$1" % typ.kind.toHumanStr
|
||||
if typ.sym != nil:
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
|
||||
result.links[j] = h.links[Mhalf + j]
|
||||
else:
|
||||
for j in 0..<Mhalf:
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result.vals[j] = move h.vals[Mhalf + j]
|
||||
else:
|
||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||
@@ -91,7 +91,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
|
||||
if less(key, h.keys[j]): break
|
||||
inc j
|
||||
for i in countdown(h.entries, j+1):
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
h.vals[i] = move h.vals[i-1]
|
||||
else:
|
||||
shallowCopy(h.vals[i], h.vals[i-1])
|
||||
|
||||
@@ -24,6 +24,7 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
|
||||
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
||||
result = false
|
||||
var n = le
|
||||
while true:
|
||||
# do NOT follow nkHiddenDeref here!
|
||||
@@ -82,6 +83,10 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ[0] != nil:
|
||||
var flags: TAssignmentFlags = {}
|
||||
if typ[0].kind in {tyOpenArray, tyVarargs}:
|
||||
# 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:
|
||||
@@ -129,7 +134,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
genAssignment(p, d, list, flags) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
@@ -137,7 +142,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, tmp, list, {}) # no need for deep copying
|
||||
genAssignment(p, tmp, list, flags) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
else:
|
||||
@@ -166,7 +171,10 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
genBoundsCheck(p, a, b, c)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
|
||||
# are mapped into ctPtrToArray, the dereference of which is skipped
|
||||
# in the `genref`. We need to skip these ptrs here
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
case ty.kind
|
||||
@@ -269,7 +277,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
|
||||
# Bug https://github.com/status-im/nimbus-eth2/issues/1549
|
||||
# Aliasing is preferred over stack overflows.
|
||||
# Also don't regress for non ARC-builds, too risky.
|
||||
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and
|
||||
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
|
||||
getSize(p.config, a.lode.typ) < 1024:
|
||||
getTemp(p, a.lode.typ, result, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
@@ -292,7 +300,8 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||
var n = if n.kind != nkHiddenAddr: n else: n[0]
|
||||
openArrayLoc(p, param.typ, n, result)
|
||||
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
|
||||
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and
|
||||
(optByRef notin param.options or not p.module.compileToCpp):
|
||||
initLocExpr(p, n, a)
|
||||
if n.kind in {nkCharLit..nkNilLit}:
|
||||
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
|
||||
@@ -312,6 +321,11 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
|
||||
addRdLoc(a, result)
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
if param.typ.kind in abstractPtrs:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
a.r = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
#assert result != nil
|
||||
|
||||
|
||||
@@ -285,15 +285,23 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
|
||||
[addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)])
|
||||
|
||||
proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
|
||||
proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
|
||||
assert d.k != locNone
|
||||
# getTemp(p, d.t, d)
|
||||
|
||||
case a.t.skipTypes(abstractVar).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(a.lode):
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
|
||||
[rdLoc(d), a.rdLoc])
|
||||
if needTempForOpenArray in flags:
|
||||
var tmp: TLoc
|
||||
getTemp(p, a.t, tmp)
|
||||
linefmt(p, cpsStmts, "$2 = $1; $n",
|
||||
[a.rdLoc, tmp.rdLoc])
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
|
||||
[rdLoc(d), tmp.rdLoc])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
|
||||
[rdLoc(d), a.rdLoc])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n",
|
||||
[rdLoc(d), a.rdLoc])
|
||||
@@ -382,7 +390,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
||||
of tyArray:
|
||||
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcOrc, gcHooks}:
|
||||
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
|
||||
genGenericAsgn(p, dest, src, flags)
|
||||
else:
|
||||
linefmt(p, cpsStmts,
|
||||
@@ -392,7 +400,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
# open arrays are always on the stack - really? What if a sequence is
|
||||
# passed to an open array?
|
||||
if reifiedOpenArray(dest.lode):
|
||||
genOpenArrayConv(p, dest, src)
|
||||
genOpenArrayConv(p, dest, src, flags)
|
||||
elif containsGarbageCollectedRef(dest.t):
|
||||
linefmt(p, cpsStmts, # XXX: is this correct for arrays?
|
||||
"#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
|
||||
@@ -401,7 +409,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts,
|
||||
# bug #4799, keep the nimCopyMem for a while
|
||||
#"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
|
||||
#"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);\n",
|
||||
"$1 = $2;$n",
|
||||
[rdLoc(dest), rdLoc(src)])
|
||||
of tySet:
|
||||
@@ -450,9 +458,10 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
|
||||
[addrLoc(p.config, dest), rdLoc(src),
|
||||
genTypeInfoV1(p.module, dest.t, dest.lode.info)])
|
||||
of tyOpenArray, tyVarargs:
|
||||
let source = addrLocOrTemp(src)
|
||||
linefmt(p, cpsStmts,
|
||||
"#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
|
||||
[addrLoc(p.config, dest), addrLocOrTemp(src),
|
||||
"#genericDeepCopyOpenArray((void*)$1, (void*)$2, $2->Field1, $3);$n",
|
||||
[addrLoc(p.config, dest), source,
|
||||
genTypeInfoV1(p.module, dest.t, dest.lode.info)])
|
||||
of tySet:
|
||||
if mapSetType(p.config, ty) == ctArray:
|
||||
@@ -615,7 +624,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
if t.kind == tyInt64: prc64[m] else: prc[m])
|
||||
putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
|
||||
else:
|
||||
let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
let res = "($1)(($2) $3 ($4))" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
|
||||
putIntoDest(p, d, e, res)
|
||||
|
||||
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
@@ -752,23 +761,8 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
|
||||
skipTypes(typ, abstractInstOwned).kind in {tyVar} and
|
||||
tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
|
||||
|
||||
proc derefBlock(p: BProc, e: PNode, d: var TLoc) =
|
||||
# We transform (block: x)[] to (block: x[])
|
||||
let e0 = e[0]
|
||||
var n = shallowCopy(e0)
|
||||
n.typ = e.typ
|
||||
for i in 0 ..< e0.len - 1:
|
||||
n[i] = e0[i]
|
||||
n[e0.len-1] = newTreeIT(nkHiddenDeref, e.info, e.typ, e0[e0.len-1])
|
||||
expr p, n, d
|
||||
|
||||
proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
||||
if e.kind == nkHiddenDeref and e[0].kind in {nkBlockExpr, nkBlockStmt}:
|
||||
# bug #20107. Watch out to not deref the pointer too late.
|
||||
derefBlock(p, e, d)
|
||||
return
|
||||
|
||||
let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]))
|
||||
let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam)
|
||||
if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
|
||||
# XXX the amount of hacks for C's arrays is incredible, maybe we should
|
||||
# simply wrap them in a struct? --> Losing auto vectorization then?
|
||||
@@ -836,7 +830,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
||||
initLocExpr(p, e[0], a)
|
||||
putIntoDest(p, d, e, "&" & a.r, a.storage)
|
||||
#Message(e.info, warnUser, "HERE NEW &")
|
||||
elif mapType(p.config, e[0].typ, mapTypeChooser(e[0])) == ctArray or isCppRef(p, e.typ):
|
||||
elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
|
||||
expr(p, e[0], d)
|
||||
else:
|
||||
var a: TLoc
|
||||
@@ -945,10 +939,17 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
|
||||
var discIndex = newRopeAppender()
|
||||
rdSetElemLoc(p.config, v, u.t, discIndex)
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
# not sure how to use `genEnumToStr` here
|
||||
if p.config.getStdlibVersion < (1, 5, 1):
|
||||
const code = "{ #raiseFieldError($1); "
|
||||
linefmt(p, cpsStmts, code, [strLit])
|
||||
let base = disc.typ.skipTypes(abstractInst+{tyRange})
|
||||
case base.kind
|
||||
of tyEnum:
|
||||
const code = "{ #raiseFieldErrorStr($1, $2); "
|
||||
let toStrProc = getToStringProc(p.module.g.graph, base)
|
||||
# XXX need to modify this logic for IC.
|
||||
# need to analyze nkFieldCheckedExpr and marks procs "used" like range checks in dce
|
||||
var toStr: TLoc
|
||||
expr(p, newSymNode(toStrProc), toStr)
|
||||
let enumStr = "$1($2)" % [rdLoc(toStr), rdLoc(v)]
|
||||
linefmt(p, cpsStmts, code, [strLit, enumStr])
|
||||
else:
|
||||
const code = "{ #raiseFieldError2($1, (NI)$2); "
|
||||
linefmt(p, cpsStmts, code, [strLit, discIndex])
|
||||
@@ -959,12 +960,8 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
|
||||
var firstLit = newRopeAppender()
|
||||
int64Literal(cast[int](first), firstLit)
|
||||
let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info)
|
||||
if p.config.getStdlibVersion < (1,5,1):
|
||||
const code = "{ #raiseFieldError($1); "
|
||||
linefmt(p, cpsStmts, code, [strLit])
|
||||
else:
|
||||
const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); "
|
||||
linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName])
|
||||
const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); "
|
||||
linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName])
|
||||
|
||||
raiseInstr(p, p.s(cpsStmts))
|
||||
linefmt p, cpsStmts, "}$n", []
|
||||
@@ -1427,7 +1424,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
|
||||
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
|
||||
|
||||
if a.storage == OnHeap and usesWriteBarrier(p.config):
|
||||
if canFormAcycle(a.t):
|
||||
if canFormAcycle(p.module.g.graph, a.t):
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc])
|
||||
@@ -1464,7 +1461,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
|
||||
var call: TLoc
|
||||
initLoc(call, locExpr, dest.lode, OnHeap)
|
||||
if dest.storage == OnHeap and usesWriteBarrier(p.config):
|
||||
if canFormAcycle(dest.t):
|
||||
if canFormAcycle(p.module.g.graph, dest.t):
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
|
||||
@@ -1507,11 +1504,12 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
|
||||
initLocExpr(p, e[1], a)
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n",
|
||||
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
||||
getSeqPayloadType(p.module, seqtype),
|
||||
])
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) # bug #22560
|
||||
putIntoDest(p, d, e, ropecg(p.module,
|
||||
"($1)#nimNewSeqOfCap($2, $3)", [
|
||||
getTypeDesc(p.module, seqtype),
|
||||
@@ -1561,6 +1559,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
var tmp: TLoc
|
||||
var r: Rope
|
||||
let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags
|
||||
if useTemp:
|
||||
getTemp(p, t, tmp)
|
||||
r = rdLoc(tmp)
|
||||
@@ -1569,10 +1568,13 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
t = t.lastSon.skipTypes(abstractInstOwned)
|
||||
r = "(*$1)" % [r]
|
||||
gcUsage(p.config, e)
|
||||
else:
|
||||
elif needsZeroMem:
|
||||
constructLoc(p, tmp)
|
||||
else:
|
||||
genObjectInit(p, cpsStmts, t, tmp, constructObj)
|
||||
else:
|
||||
resetLoc(p, d)
|
||||
if needsZeroMem: resetLoc(p, d)
|
||||
else: genObjectInit(p, cpsStmts, d.t, d, if isRef: constructRefObj else: constructObj)
|
||||
r = rdLoc(d)
|
||||
discard getTypeDesc(p.module, t)
|
||||
let ty = getUniqueType(t)
|
||||
@@ -1887,16 +1889,25 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, m, b, c)
|
||||
if op == mHigh:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1)+1)", [rdLoc(b), rdLoc(c)]))
|
||||
else:
|
||||
if not reifiedOpenArray(a):
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
|
||||
else: unaryExpr(p, e, d, "$1Len_0")
|
||||
else:
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)")
|
||||
else: unaryExpr(p, e, d, "$1.Field1")
|
||||
let isDeref = a.kind in {nkHiddenDeref, nkDerefExpr}
|
||||
if op == mHigh:
|
||||
if isDeref:
|
||||
unaryExpr(p, e, d, "($1->Field1-1)")
|
||||
else:
|
||||
unaryExpr(p, e, d, "($1.Field1-1)")
|
||||
else:
|
||||
if isDeref:
|
||||
unaryExpr(p, e, d, "$1->Field1")
|
||||
else:
|
||||
unaryExpr(p, e, d, "$1.Field1")
|
||||
of tyCstring:
|
||||
if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
|
||||
else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
|
||||
@@ -1921,10 +1932,6 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
|
||||
else: internalError(p.config, e.info, "genArrayLen()")
|
||||
|
||||
proc makePtrType(baseType: PType; idgen: IdGenerator): PType =
|
||||
result = newType(tyPtr, nextTypeId idgen, baseType.owner)
|
||||
addSonSkipIntLit(result, baseType, idgen)
|
||||
|
||||
proc makeAddr(n: PNode; idgen: IdGenerator): PNode =
|
||||
if n.kind == nkHiddenAddr:
|
||||
result = n
|
||||
@@ -1947,7 +1954,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
initLoc(call, locCall, e, OnHeap)
|
||||
if not p.module.compileToCpp:
|
||||
const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
|
||||
const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
|
||||
call.r = ropecg(p.module, setLenPattern, [
|
||||
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
|
||||
@@ -2126,7 +2133,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mCard:
|
||||
var a: TLoc
|
||||
initLocExpr(p, e[1], a)
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size]))
|
||||
putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size]))
|
||||
of mLtSet, mLeSet:
|
||||
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
|
||||
initLocExpr(p, e[1], a)
|
||||
@@ -2207,8 +2214,15 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
var lbl = p.labels.rope
|
||||
var tmp: TLoc
|
||||
tmp.r = "LOC$1.source" % [lbl]
|
||||
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
|
||||
[getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
|
||||
let destsize = getSize(p.config, destt)
|
||||
let srcsize = getSize(p.config, srct)
|
||||
|
||||
if destsize > srcsize:
|
||||
linefmt(p, cpsLocals, "union { $1 dest; $2 source; } LOC$3;$n #nimZeroMem(&LOC$3, sizeof(LOC$3));$n",
|
||||
[getTypeDesc(p.module, e.typ), getTypeDesc(p.module, e[1].typ), lbl])
|
||||
else:
|
||||
linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
|
||||
[getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
|
||||
tmp.k = locExpr
|
||||
tmp.lode = lodeTyp srct
|
||||
tmp.storage = OnStack
|
||||
@@ -2339,7 +2353,7 @@ proc genWasMoved(p: BProc; n: PNode) =
|
||||
if p.withinBlockLeaveActions > 0 and notYetAlive(n1):
|
||||
discard
|
||||
else:
|
||||
initLocExpr(p, n1, a)
|
||||
initLocExpr(p, n1, a, {lfEnforceDeref})
|
||||
resetLoc(p, a)
|
||||
#linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
||||
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
|
||||
@@ -2356,8 +2370,32 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, n.typ, d)
|
||||
genAssignment(p, d, a, {})
|
||||
resetLoc(p, a)
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
genAssignment(p, d, a, {})
|
||||
var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved)
|
||||
if op == nil:
|
||||
resetLoc(p, a)
|
||||
else:
|
||||
var b: TLoc
|
||||
initLocExpr(p, newSymNode(op), b)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for
|
||||
# openarrays, but it probably shouldn't?
|
||||
var s: string
|
||||
if reifiedOpenArray(a.lode):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
s = "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
else:
|
||||
s = "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
else:
|
||||
s = "$1, $1Len_0" % [rdLoc(a)]
|
||||
linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)])
|
||||
else:
|
||||
let flags = if not canMove(p, n[1], d): {needToCopy} else: {}
|
||||
genAssignment(p, d, a, flags)
|
||||
resetLoc(p, a)
|
||||
|
||||
proc genDestroy(p: BProc; n: PNode) =
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
@@ -2411,7 +2449,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) =
|
||||
let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon,
|
||||
prepareForMutation = e[1].kind == nkHiddenDeref and
|
||||
e[1].typ.skipTypes(abstractInst).kind == tyString and
|
||||
p.config.selectedGC in {gcArc, gcOrc})
|
||||
p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc})
|
||||
if d.k == locNone: getTemp(p, e.typ, d)
|
||||
linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y])
|
||||
when false:
|
||||
@@ -2518,10 +2556,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
|
||||
of mSizeOf:
|
||||
let t = e[1].typ.skipTypes({tyTypeDesc})
|
||||
putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, skVar)])
|
||||
putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, dkVar)])
|
||||
of mAlignOf:
|
||||
let t = e[1].typ.skipTypes({tyTypeDesc})
|
||||
putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, skVar)])
|
||||
putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, dkVar)])
|
||||
of mOffsetOf:
|
||||
var dotExpr: PNode
|
||||
if e[1].kind == nkDotExpr:
|
||||
@@ -2531,7 +2569,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else:
|
||||
internalError(p.config, e.info, "unknown ast")
|
||||
let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
|
||||
let tname = getTypeDesc(p.module, t, skVar)
|
||||
let tname = getTypeDesc(p.module, t, dkVar)
|
||||
let member =
|
||||
if t.kind == tyTuple:
|
||||
"Field" & rope(dotExpr[1].sym.position)
|
||||
@@ -2593,9 +2631,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
|
||||
expr(p, n, d)
|
||||
of mDeepCopy:
|
||||
if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
|
||||
localError(p.config, e.info,
|
||||
"for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
|
||||
"for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
|
||||
|
||||
var a, b: TLoc
|
||||
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
|
||||
@@ -2610,6 +2648,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mAccessTypeField: genAccessTypeField(p, e, d)
|
||||
of mSlice: genSlice(p, e, d)
|
||||
of mTrace: discard "no code to generate"
|
||||
of mEnsureMove:
|
||||
expr(p, e[1], d)
|
||||
else:
|
||||
when defined(debugMagics):
|
||||
echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
|
||||
@@ -2680,15 +2720,30 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if not handleConstExpr(p, n, d):
|
||||
let t = n.typ
|
||||
discard getTypeDesc(p.module, t) # so that any fields are initialized
|
||||
if d.k == locNone: getTemp(p, t, d)
|
||||
|
||||
var tmp: TLoc
|
||||
# bug #16331
|
||||
let doesAlias = lhsDoesAlias(d.lode, n)
|
||||
let dest = if doesAlias: addr(tmp) else: addr(d)
|
||||
if doesAlias:
|
||||
getTemp(p, n.typ, tmp)
|
||||
elif d.k == locNone:
|
||||
getTemp(p, n.typ, d)
|
||||
|
||||
for i in 0..<n.len:
|
||||
var it = n[i]
|
||||
if it.kind == nkExprColonExpr: it = it[1]
|
||||
initLoc(rec, locExpr, it, d.storage)
|
||||
rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
|
||||
initLoc(rec, locExpr, it, dest[].storage)
|
||||
rec.r = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
|
||||
rec.flags.incl(lfEnforceDeref)
|
||||
expr(p, it, rec)
|
||||
|
||||
if doesAlias:
|
||||
if d.k == locNone:
|
||||
d = tmp
|
||||
else:
|
||||
genAssignment(p, d, tmp, {})
|
||||
|
||||
proc isConstClosure(n: PNode): bool {.inline.} =
|
||||
result = n[0].kind == nkSym and isRoutine(n[0].sym) and
|
||||
n[1].kind == nkNilLit
|
||||
@@ -2846,7 +2901,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
|
||||
# expression not found in the cache:
|
||||
inc(p.module.labels)
|
||||
p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ",
|
||||
[getTypeDesc(p.module, t, skConst), tmp])
|
||||
[getTypeDesc(p.module, t, dkConst), tmp])
|
||||
genBracedInit(p, n, isConst = true, t, p.module.s[cfsData])
|
||||
p.module.s[cfsData].addf(";$n", [])
|
||||
|
||||
@@ -2873,13 +2928,13 @@ proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
|
||||
if not genConstSetup(p, sym): return
|
||||
assert(sym.loc.r != "", $sym.name.s & $sym.itemId)
|
||||
if m.hcrOn:
|
||||
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
|
||||
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]);
|
||||
m.initProc.procSec(cpsLocals).addf(
|
||||
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
||||
getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
|
||||
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)])
|
||||
else:
|
||||
let headerDecl = "extern NIM_CONST $1 $2;$n" %
|
||||
[getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
|
||||
[getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]
|
||||
m.s[cfsData].add(headerDecl)
|
||||
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
|
||||
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
|
||||
@@ -2895,7 +2950,7 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
|
||||
q.s[cfsData].add data
|
||||
if q.hcrOn:
|
||||
# generate the global pointer with the real name
|
||||
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r])
|
||||
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r])
|
||||
# register it (but ignore the boolean result of hcrRegisterGlobal)
|
||||
q.initProc.procSec(cpsLocals).addf(
|
||||
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
|
||||
@@ -3380,18 +3435,19 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope)
|
||||
|
||||
proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
|
||||
let base = t.skipTypes(abstractInst)[0]
|
||||
var data = rope"{"
|
||||
for i in 0..<n.len:
|
||||
if i > 0: data.addf(",$n", [])
|
||||
genBracedInit(p, n[i], isConst, base, data)
|
||||
data.add("}")
|
||||
var data = rope""
|
||||
if n.len > 0:
|
||||
data.add(", {")
|
||||
for i in 0..<n.len:
|
||||
if i > 0: data.addf(",$n", [])
|
||||
genBracedInit(p, n[i], isConst, base, data)
|
||||
data.add("}")
|
||||
|
||||
let payload = getTempName(p.module)
|
||||
|
||||
appcg(p.module, cfsStrData,
|
||||
"static $5 struct {$n" &
|
||||
" NI cap; $1 data[$2];$n" &
|
||||
"} $3 = {$2 | NIM_STRLIT_FLAG, $4};$n", [
|
||||
"} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [
|
||||
getTypeDesc(p.module, base), n.len, payload, data,
|
||||
if isConst: "const" else: ""])
|
||||
result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload]
|
||||
|
||||
@@ -83,7 +83,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
else:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
of tyChar, tyBool, tyEnum, tyInt..tyUInt64:
|
||||
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
@@ -97,7 +97,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
else:
|
||||
doAssert false, "unexpected set type kind"
|
||||
of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
||||
tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
|
||||
tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
|
||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
|
||||
|
||||
@@ -35,7 +35,9 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
|
||||
if n.kind == nkEmpty:
|
||||
result = false
|
||||
elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc:
|
||||
if isInvalidReturnType(conf, n[0].typ, true):
|
||||
if n[0].kind == nkSym and sfConstructor in n[0].sym.flags:
|
||||
result = true
|
||||
elif isInvalidReturnType(conf, n[0].typ, true):
|
||||
# var v = f()
|
||||
# is transformed into: var v; f(addr v)
|
||||
# where 'f' **does not** initialize the result!
|
||||
@@ -288,11 +290,28 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
|
||||
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
|
||||
genBracedInit(p, value, isConst = false, v.typ, result)
|
||||
|
||||
proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) =
|
||||
var params = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
let typ = skipTypes(value[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
for i in 1..<value.len:
|
||||
assert(typ.len == typ.n.len)
|
||||
genOtherArg(p, value, i, typ, params, argsCounter)
|
||||
if params.len == 0:
|
||||
decl = runtimeFormat("$#;\n", [decl])
|
||||
else:
|
||||
decl = runtimeFormat("$#($#);\n", [decl, params])
|
||||
|
||||
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
if sfGoto in v.flags:
|
||||
# translate 'var state {.goto.} = X' into 'goto LX':
|
||||
genGotoVar(p, value)
|
||||
return
|
||||
let imm = isAssignedImmediately(p.config, value)
|
||||
let isCppCtorCall = p.module.compileToCpp and imm and
|
||||
value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||
v.typ.kind != tyPtr and sfConstructor in value[0].sym.flags
|
||||
var targetProc = p
|
||||
var valueAsRope = ""
|
||||
potentialValueInit(p, v, value, valueAsRope)
|
||||
@@ -304,7 +323,11 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
if sfPure in v.flags:
|
||||
# v.owner.kind != skModule:
|
||||
targetProc = p.module.preInitProc
|
||||
assignGlobalVar(targetProc, vn, valueAsRope)
|
||||
if isCppCtorCall and not containsHiddenPointer(v.typ):
|
||||
callGlobalVarCppCtor(targetProc, v, vn, value)
|
||||
else:
|
||||
assignGlobalVar(targetProc, vn, valueAsRope)
|
||||
|
||||
# XXX: be careful here.
|
||||
# Global variables should not be zeromem-ed within loops
|
||||
# (see bug #20).
|
||||
@@ -313,7 +336,6 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
# global variables will be initialized to zero.
|
||||
if valueAsRope.len == 0:
|
||||
var loc = v.loc
|
||||
|
||||
# When the native TLS is unavailable, a global thread-local variable needs
|
||||
# one more layer of indirection in order to access the TLS block.
|
||||
# Only do this for complex types that may need a call to `objectInit`
|
||||
@@ -327,31 +349,21 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
genVarPrototype(p.module.g.generatedHeader, vn)
|
||||
registerTraverseProc(p, v)
|
||||
else:
|
||||
let imm = isAssignedImmediately(p.config, value)
|
||||
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
||||
not containsHiddenPointer(v.typ):
|
||||
not containsHiddenPointer(v.typ) and
|
||||
nimErrorFlagAccessed notin p.flags:
|
||||
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
|
||||
# parameterless constructor followed by an assignment operator. So we
|
||||
# generate better code here: 'Foo f = x;'
|
||||
genLineDir(p, vn)
|
||||
let decl = localVarDecl(p, vn)
|
||||
var decl = localVarDecl(p, vn)
|
||||
var tmp: TLoc
|
||||
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||
sfConstructor in value[0].sym.flags:
|
||||
var params = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
let typ = skipTypes(value[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
for i in 1..<value.len:
|
||||
assert(typ.len == typ.n.len)
|
||||
genOtherArg(p, value, i, typ, params, argsCounter)
|
||||
if params.len == 0:
|
||||
lineF(p, cpsStmts, "$#;$n", [decl])
|
||||
else:
|
||||
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
|
||||
if isCppCtorCall:
|
||||
genCppVarForCtor(p, v, vn, value, decl)
|
||||
line(p, cpsStmts, decl)
|
||||
else:
|
||||
initLocExprSingleUse(p, value, tmp)
|
||||
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
|
||||
lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
|
||||
return
|
||||
assignLocalVar(p, vn)
|
||||
initLocalVar(p, v, imm)
|
||||
@@ -378,7 +390,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
startBlock(targetProc)
|
||||
if value.kind != nkEmpty and valueAsRope.len == 0:
|
||||
genLineDir(targetProc, vn)
|
||||
loadInto(targetProc, vn, value, v.loc)
|
||||
if not isCppCtorCall:
|
||||
loadInto(targetProc, vn, value, v.loc)
|
||||
if forHcr:
|
||||
endBlock(targetProc)
|
||||
|
||||
@@ -613,7 +626,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
|
||||
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
|
||||
lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
|
||||
assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
|
||||
lineF(p, cpsStmts, ";$n", [])
|
||||
appcg(p, cpsStmts, ";$n", [])
|
||||
genStmts(p, loopBody)
|
||||
|
||||
if optProfiler in p.options:
|
||||
@@ -653,7 +666,7 @@ proc genParForStmt(p: BProc, t: PNode) =
|
||||
#initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
|
||||
#discard mangleName(forLoopVar)
|
||||
let call = t[1]
|
||||
assert(call.len in {4, 5})
|
||||
assert(call.len == 4 or call.len == 5)
|
||||
initLocExpr(p, call[1], rangeA)
|
||||
initLocExpr(p, call[2], rangeB)
|
||||
|
||||
@@ -766,11 +779,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
else:
|
||||
finallyActions(p)
|
||||
genLineDir(p, t)
|
||||
# reraise the last exception:
|
||||
if p.config.exc == excCpp:
|
||||
line(p, cpsStmts, "throw;\n")
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||
raiseInstr(p, p.s(cpsStmts))
|
||||
|
||||
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||
@@ -962,8 +971,11 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||
hasDefault = true
|
||||
exprBlock(p, branch.lastSon, d)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
if not hasDefault:
|
||||
if hasBuiltinUnreachable in CC[p.config.cCompiler].props:
|
||||
lineF(p, cpsStmts, "default: __builtin_unreachable();$n", [])
|
||||
elif hasAssume in CC[p.config.cCompiler].props:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
if lend != "": fixLabel(p, lend)
|
||||
|
||||
@@ -996,7 +1008,7 @@ proc genRestoreFrameAfterException(p: BProc) =
|
||||
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
#[ code to generate:
|
||||
|
||||
std::exception_ptr error = nullptr;
|
||||
std::exception_ptr error;
|
||||
try {
|
||||
body;
|
||||
} catch (Exception e) {
|
||||
@@ -1027,7 +1039,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
inc(p.labels, 2)
|
||||
let etmp = p.labels
|
||||
|
||||
p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp]))
|
||||
lineCg(p, cpsStmts, "std::exception_ptr T$1_;$n", [etmp])
|
||||
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
@@ -1061,7 +1073,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
if hasIf: lineF(p, cpsStmts, "else ", [])
|
||||
startBlock(p)
|
||||
# we handled the error:
|
||||
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
|
||||
expr(p, t[i][0], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
endBlock(p)
|
||||
@@ -1124,7 +1135,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
|
||||
if t[i].len == 1:
|
||||
# general except section:
|
||||
startBlock(p, "catch (...) {", [])
|
||||
startBlock(p, "catch (...) {$n", [])
|
||||
genExceptBranchBody(t[i][0])
|
||||
endBlock(p)
|
||||
catchAllPresent = true
|
||||
@@ -1150,7 +1161,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
# general finally block:
|
||||
if t.len > 0 and t[^1].kind == nkFinally:
|
||||
if not catchAllPresent:
|
||||
startBlock(p, "catch (...) {", [])
|
||||
startBlock(p, "catch (...) {$n", [])
|
||||
genRestoreFrameAfterException(p)
|
||||
linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
|
||||
endBlock(p)
|
||||
@@ -1300,7 +1311,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
let checkFor = $getObjDepth(t[i][j].typ)
|
||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
|
||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
|
||||
[memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
|
||||
else:
|
||||
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
@@ -1622,7 +1634,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||
let le = e[0]
|
||||
let ri = e[1]
|
||||
var a: TLoc
|
||||
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar)
|
||||
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar)
|
||||
initLoc(a, locNone, le, OnUnknown)
|
||||
a.flags.incl(lfEnforceDeref)
|
||||
a.flags.incl(lfPrepareForMutation)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,8 @@ import
|
||||
ast, types, hashes, strutils, msgs, wordrecg,
|
||||
platform, trees, options, cgendata
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
@@ -122,13 +124,19 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
var pt = skipTypes(s.typ, typedescInst)
|
||||
assert skResult != s.kind
|
||||
|
||||
if tfByRef in pt.flags: return true
|
||||
#note precedence: params override types
|
||||
if optByRef in s.options: return true
|
||||
elif sfByCopy in s.flags: return false
|
||||
elif tfByRef in pt.flags: return true
|
||||
elif tfByCopy in pt.flags: return false
|
||||
case pt.kind
|
||||
of tyObject:
|
||||
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):
|
||||
@@ -145,3 +153,64 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
|
||||
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
|
||||
|
||||
proc encodeName*(name: string): string =
|
||||
result = mangle(name)
|
||||
result = $result.len & result
|
||||
|
||||
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result = if name == "": s.name.s else: name
|
||||
result.add "__"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
#Module::Type
|
||||
var name = s.name.s
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
case t.kind
|
||||
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
|
||||
result = encodeSym(m, t.sym)
|
||||
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
|
||||
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 "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
else: encodeName(kindName)
|
||||
result.add "I"
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s)
|
||||
result.add "E"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
val.addFloat t.n[0].floatVal
|
||||
val.add "_"
|
||||
val.addFloat t.n[1].floatVal
|
||||
else:
|
||||
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
|
||||
result = encodeName(val)
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
|
||||
@@ -30,6 +30,12 @@ import strutils except `%`, addf # collides with ropes.`%`
|
||||
from ic / ic import ModuleBackendFlag
|
||||
import dynlib
|
||||
|
||||
const
|
||||
# we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
|
||||
postprocessDirStart = '\1'
|
||||
postprocessDirSep = '\31'
|
||||
postprocessDirEnd = '\23'
|
||||
|
||||
when not declared(dynlib.libCandidates):
|
||||
proc libCandidates(s: string, dest: var seq[string]) =
|
||||
## given a library name pattern `s` write possible library names to `dest`.
|
||||
@@ -61,12 +67,12 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
|
||||
var ms = getModule(s)
|
||||
result = m.g.modules[ms.position]
|
||||
|
||||
proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
|
||||
proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}) =
|
||||
result.k = k
|
||||
result.storage = s
|
||||
result.lode = lode
|
||||
result.r = ""
|
||||
result.flags = {}
|
||||
result.flags = flags
|
||||
|
||||
proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
|
||||
# fills the loc if it is not already initialized
|
||||
@@ -216,13 +222,9 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
|
||||
elif frmt[i] == '#' and frmt[i+1] == '#':
|
||||
inc(i, 2)
|
||||
strLit.add("#")
|
||||
|
||||
var start = i
|
||||
while i < frmt.len:
|
||||
if frmt[i] != '$' and frmt[i] != '#': inc(i)
|
||||
else: break
|
||||
if i - 1 >= start:
|
||||
strLit.add(substr(frmt, start, i - 1))
|
||||
else:
|
||||
strLit.add(frmt[i])
|
||||
inc(i)
|
||||
|
||||
flushStrLit()
|
||||
result.add newCall(ident"rope", resVar)
|
||||
@@ -270,15 +272,28 @@ proc safeLineNm(info: TLineInfo): int =
|
||||
result = toLinenumber(info)
|
||||
if result < 0: result = 0 # negative numbers are not allowed in #line
|
||||
|
||||
proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
|
||||
proc genPostprocessDir(field1, field2, field3: string): string =
|
||||
result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
|
||||
|
||||
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
|
||||
assert line >= 0
|
||||
if optLineDir in conf.options and line > 0:
|
||||
r.addf("$N#line $2 $1$N",
|
||||
[rope(makeSingleLineCString(filename)), rope(line)])
|
||||
if fileIdx == InvalidFileIdx:
|
||||
r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
|
||||
else:
|
||||
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
|
||||
|
||||
proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
|
||||
assert line >= 0
|
||||
if optLineDir in p.config.options and line > 0:
|
||||
if fileIdx == InvalidFileIdx:
|
||||
r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
|
||||
else:
|
||||
r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
|
||||
|
||||
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
|
||||
if optLineDir in conf.options:
|
||||
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
|
||||
genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
|
||||
|
||||
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
|
||||
if p.lastLineInfo.line != info.line or
|
||||
@@ -287,17 +302,25 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
|
||||
p.lastLineInfo.fileIndex = info.fileIndex
|
||||
result = true
|
||||
|
||||
proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
|
||||
if optLineDir in conf.options:
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
if freshLineInfo(p, info):
|
||||
genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
|
||||
|
||||
proc genLineDir(p: BProc, t: PNode) =
|
||||
let line = t.info.safeLineNm
|
||||
|
||||
if optEmbedOrigSrc in p.config.globalOptions:
|
||||
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
|
||||
genCLineDir(p.s(cpsStmts), t.info, p.config)
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
let freshLine = freshLineInfo(p, t.info)
|
||||
if freshLine:
|
||||
genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex)
|
||||
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
|
||||
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
||||
if freshLineInfo(p, t.info):
|
||||
linefmt(p, cpsStmts, "nimln_($1, $2);$n",
|
||||
[line, quotedFilename(p.config, t.info)])
|
||||
if freshLine:
|
||||
line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32))
|
||||
|
||||
proc accessThreadLocalVar(p: BProc, s: PSym)
|
||||
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
|
||||
@@ -356,19 +379,19 @@ template mapTypeChooser(n: PNode): TSymKind =
|
||||
template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
|
||||
|
||||
proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
|
||||
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
|
||||
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
|
||||
result.add "(&" & a.r & ")"
|
||||
else:
|
||||
result.add a.r
|
||||
|
||||
proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
|
||||
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
|
||||
if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
|
||||
result = "(&" & a.r & ")"
|
||||
else:
|
||||
result = a.r
|
||||
|
||||
proc byRefLoc(p: BProc; a: TLoc): Rope =
|
||||
if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not
|
||||
if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not
|
||||
p.module.compileToCpp:
|
||||
result = "(&" & a.r & ")"
|
||||
else:
|
||||
@@ -383,6 +406,7 @@ proc rdCharLoc(a: TLoc): Rope =
|
||||
type
|
||||
TAssignmentFlag = enum
|
||||
needToCopy
|
||||
needTempForOpenArray
|
||||
TAssignmentFlags = set[TAssignmentFlag]
|
||||
|
||||
proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
|
||||
@@ -420,7 +444,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
|
||||
rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
|
||||
linefmt(p, cpsStmts,
|
||||
"#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
|
||||
[rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, mapTypeChooser(a))])
|
||||
[rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, descKindFromSymKind mapTypeChooser(a))])
|
||||
else:
|
||||
rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
|
||||
genAssignment(p, a, tmp, {})
|
||||
@@ -482,14 +506,11 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
# so we use getTypeDesc here rather than rdLoc(loc)
|
||||
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
||||
[addrLoc(p.config, loc),
|
||||
getTypeDesc(p.module, loc.t, mapTypeChooser(loc))])
|
||||
getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))])
|
||||
# XXX: We can be extra clever here and call memset only
|
||||
# on the bytes following the m_type field?
|
||||
genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
|
||||
|
||||
proc isOrHasImportedCppType(typ: PType): bool =
|
||||
searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)
|
||||
|
||||
proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
|
||||
let typ = loc.t
|
||||
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
|
||||
@@ -502,14 +523,14 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
|
||||
genRefAssign(p, loc, nilLoc)
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
|
||||
getTypeDesc(p.module, typ, mapTypeChooser(loc))])
|
||||
getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
|
||||
else:
|
||||
if not isTemp or containsGarbageCollectedRef(loc.t):
|
||||
# don't use nimZeroMem for temporary values for performance if we can
|
||||
# avoid it:
|
||||
if not isOrHasImportedCppType(typ):
|
||||
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
||||
[addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))])
|
||||
[addrLoc(p.config, loc), getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
|
||||
genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
|
||||
|
||||
proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
|
||||
@@ -528,9 +549,9 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
|
||||
inc(p.labels)
|
||||
result.r = "T" & rope(p.labels) & "_"
|
||||
if p.module.compileToCpp and isOrHasImportedCppType(t):
|
||||
linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, skVar), result.r])
|
||||
linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, dkVar), result.r])
|
||||
else:
|
||||
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r])
|
||||
linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r])
|
||||
result.k = locTemp
|
||||
result.lode = lodeTyp t
|
||||
result.storage = OnStack
|
||||
@@ -548,7 +569,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
|
||||
proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
|
||||
inc(p.labels)
|
||||
result.r = "T" & rope(p.labels) & "_"
|
||||
linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, skVar), result.r, value])
|
||||
linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value])
|
||||
result.k = locTemp
|
||||
result.lode = lodeTyp t
|
||||
result.storage = OnStack
|
||||
@@ -572,10 +593,10 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||
result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
|
||||
|
||||
genCLineDir(result, n.info, p.config)
|
||||
genCLineDir(result, p, n.info, p.config)
|
||||
|
||||
result.add getTypeDesc(p.module, s.typ, skVar)
|
||||
if s.constraint.isNil:
|
||||
result.add getTypeDesc(p.module, s.typ, dkVar)
|
||||
if sfCodegenDecl notin s.flags:
|
||||
if sfRegister in s.flags: result.add(" register")
|
||||
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
|
||||
# decl.add(" GC_GUARD")
|
||||
@@ -590,7 +611,7 @@ proc assignLocalVar(p: BProc, n: PNode) =
|
||||
#assert(s.loc.k == locNone) # not yet assigned
|
||||
# this need not be fulfilled for inline procs; they are regenerated
|
||||
# for each module that uses them!
|
||||
let nl = if optLineDir in p.config.options: "" else: "\L"
|
||||
let nl = if optLineDir in p.config.options: "" else: "\n"
|
||||
let decl = localVarDecl(p, n) & (if p.module.compileToCpp and isOrHasImportedCppType(n.typ): "{};" else: ";") & nl
|
||||
line(p, cpsLocals, decl)
|
||||
|
||||
@@ -604,6 +625,40 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
|
||||
# and s.owner.kind == skModule # owner isn't always a module (global pragma on local var)
|
||||
# and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used
|
||||
|
||||
proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
|
||||
let s = n.sym
|
||||
if sfCodegenDecl notin s.flags:
|
||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||
decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
|
||||
if p.hcrOn: decl.add("static ")
|
||||
elif sfImportc in s.flags: decl.add("extern ")
|
||||
elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
|
||||
else: decl.add("N_LIB_PRIVATE ")
|
||||
if s.kind == skLet and value != "": decl.add("NIM_CONST ")
|
||||
decl.add(td)
|
||||
if p.hcrOn: decl.add("*")
|
||||
if sfRegister in s.flags: decl.add(" register")
|
||||
if sfVolatile in s.flags: decl.add(" volatile")
|
||||
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
|
||||
else:
|
||||
if value != "":
|
||||
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
|
||||
else:
|
||||
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
|
||||
|
||||
proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope)
|
||||
|
||||
proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) =
|
||||
let s = vn.sym
|
||||
fillBackendName(p.module, s)
|
||||
fillLoc(s.loc, locGlobalVar, vn, OnHeap)
|
||||
var decl: Rope
|
||||
let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
|
||||
genGlobalVarDecl(p, vn, td, "", decl)
|
||||
decl.add " " & $s.loc.r
|
||||
genCppVarForCtor(p, v, vn, value, decl)
|
||||
p.module.s[cfsVars].add decl
|
||||
|
||||
proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
|
||||
let s = n.sym
|
||||
if s.loc.k == locNone:
|
||||
@@ -629,29 +684,30 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
|
||||
internalError(p.config, n.info, ".threadvar variables cannot have a value")
|
||||
else:
|
||||
var decl: Rope = ""
|
||||
var td = getTypeDesc(p.module, s.loc.t, skVar)
|
||||
let td = getTypeDesc(p.module, s.loc.t, dkVar)
|
||||
genGlobalVarDecl(p, n, td, value, decl)
|
||||
if s.constraint.isNil:
|
||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||
decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
|
||||
if p.hcrOn: decl.add("static ")
|
||||
elif sfImportc in s.flags: decl.add("extern ")
|
||||
elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
|
||||
else: decl.add("N_LIB_PRIVATE ")
|
||||
if s.kind == skLet and value != "": decl.add("NIM_CONST ")
|
||||
decl.add(td)
|
||||
if p.hcrOn: decl.add("*")
|
||||
if sfRegister in s.flags: decl.add(" register")
|
||||
if sfVolatile in s.flags: decl.add(" volatile")
|
||||
if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
|
||||
if value != "":
|
||||
decl.addf(" $1 = $2;$n", [s.loc.r, value])
|
||||
if p.module.compileToCpp and value.startsWith "{{}":
|
||||
# TODO: taking this branch, re"\{\{\}(,\s\{\})*\}" might be emitted, resulting in
|
||||
# either warnings (GCC 12.2+) or errors (Clang 15, MSVC 19.3+) of C++11+ compilers **when
|
||||
# explicit constructors are around** due to overload resolution rules in place [^0][^1][^2]
|
||||
# *Workaround* here: have C++'s static initialization mechanism do the default init work,
|
||||
# for us lacking a deeper knowledge of an imported object's constructors' ex-/implicitness
|
||||
# (so far) *and yet* trying to achieve default initialization.
|
||||
# Still, generating {}s in genConstObjConstr() just to omit them here is faaaar from ideal;
|
||||
# need to figure out a better way, possibly by keeping around more data about the
|
||||
# imported objects' contructors?
|
||||
#
|
||||
# [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization
|
||||
# [^1]: https://cplusplus.github.io/CWG/issues/1518.html
|
||||
# [^2]: https://eel.is/c++draft/over.match.ctor
|
||||
decl.addf(" $1;$n", [s.loc.r])
|
||||
else:
|
||||
decl.addf(" $1 = $2;$n", [s.loc.r, value])
|
||||
else:
|
||||
decl.addf(" $1;$n", [s.loc.r])
|
||||
else:
|
||||
if value != "":
|
||||
decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
|
||||
else:
|
||||
decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
|
||||
|
||||
p.module.s[cfsVars].add(decl)
|
||||
if p.withinLoop > 0 and value == "":
|
||||
# fixes tests/run/tzeroarray:
|
||||
@@ -672,7 +728,7 @@ proc getLabel(p: BProc): TLabel =
|
||||
result = "LA" & rope(p.labels) & "_"
|
||||
|
||||
proc fixLabel(p: BProc, labl: TLabel) =
|
||||
lineF(p, cpsStmts, "$1: ;$n", [labl])
|
||||
p.s(cpsStmts).add("$1: ;$n" % [labl])
|
||||
|
||||
proc genVarPrototype(m: BModule, n: PNode)
|
||||
proc requestConstImpl(p: BProc, sym: PSym)
|
||||
@@ -685,8 +741,8 @@ proc genLiteral(p: BProc, n: PNode; result: var Rope)
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
|
||||
proc raiseExit(p: BProc)
|
||||
|
||||
proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
|
||||
initLoc(result, locNone, e, OnUnknown)
|
||||
proc initLocExpr(p: BProc, e: PNode, result: var TLoc, flags: TLocFlags = {}) =
|
||||
initLoc(result, locNone, e, OnUnknown, flags)
|
||||
expr(p, e, result)
|
||||
|
||||
proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
|
||||
@@ -714,8 +770,12 @@ $1define nimfr_(proc, file) \
|
||||
struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \
|
||||
FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
|
||||
|
||||
$1define nimln_(n, file) \
|
||||
$1define nimln_(n) \
|
||||
FR_.line = n;
|
||||
|
||||
$1define nimlf_(n, file) \
|
||||
FR_.line = n; FR_.filename = file;
|
||||
|
||||
"""
|
||||
if p.module.s[cfsFrameDefines].len == 0:
|
||||
appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
|
||||
@@ -781,7 +841,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
|
||||
initLoc(dest, locTemp, lib.path, OnStack)
|
||||
dest.r = getTempName(m)
|
||||
appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
|
||||
[getTypeDesc(m, lib.path.typ, skVar), rdLoc(dest)])
|
||||
[getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
|
||||
expr(p, lib.path, dest)
|
||||
|
||||
m.s[cfsVars].add(p.s(cpsLocals))
|
||||
@@ -821,7 +881,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
|
||||
params.add(rdLoc(a))
|
||||
params.add(", ")
|
||||
let load = "\t$1 = ($2) ($3$4));$n" %
|
||||
[tmp, getTypeDesc(m, sym.typ, skVar), params, makeCString($extname)]
|
||||
[tmp, getTypeDesc(m, sym.typ, dkVar), params, makeCString($extname)]
|
||||
var last = lastSon(n)
|
||||
if last.kind == nkHiddenStdConv: last = last[1]
|
||||
internalAssert(m.config, last.kind == nkStrLit)
|
||||
@@ -835,8 +895,8 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
|
||||
else:
|
||||
appcg(m, m.s[cfsDynLibInit],
|
||||
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
|
||||
[tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
|
||||
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
|
||||
[tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
|
||||
m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
|
||||
|
||||
proc varInDynamicLib(m: BModule, sym: PSym) =
|
||||
var lib = sym.annex
|
||||
@@ -848,9 +908,9 @@ proc varInDynamicLib(m: BModule, sym: PSym) =
|
||||
inc(m.labels, 2)
|
||||
appcg(m, m.s[cfsDynLibInit],
|
||||
"$1 = ($2*) #nimGetProcAddr($3, $4);$n",
|
||||
[tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
|
||||
[tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
|
||||
m.s[cfsVars].addf("$2* $1;$n",
|
||||
[sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
|
||||
[sym.loc.r, getTypeDesc(m, sym.loc.t, dkVar)])
|
||||
|
||||
proc symInDynamicLibPartial(m: BModule, sym: PSym) =
|
||||
sym.loc.r = mangleDynLibProc(sym)
|
||||
@@ -881,7 +941,9 @@ proc cgsymValue(m: BModule, name: string): Rope =
|
||||
result.addActualSuffixForHCR(m.module, sym)
|
||||
|
||||
proc generateHeaders(m: BModule) =
|
||||
m.s[cfsHeaders].add("\L#include \"nimbase.h\"\L")
|
||||
var nimbase = m.config.nimbasePattern
|
||||
if nimbase == "": nimbase = "nimbase.h"
|
||||
m.s[cfsHeaders].addf("\L#include \"$1\"\L", [nimbase])
|
||||
|
||||
for it in m.headerFiles:
|
||||
if it[0] == '#':
|
||||
@@ -930,11 +992,19 @@ proc closureSetup(p: BProc, prc: PSym) =
|
||||
linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
|
||||
[rdLoc(env.loc), getTypeDesc(p.module, env.typ)])
|
||||
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
|
||||
nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
|
||||
declarativeDefs
|
||||
|
||||
proc containsResult(n: PNode): bool =
|
||||
result = false
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams:
|
||||
of succ(nkEmpty)..pred(nkSym), succ(nkSym)..nkNilLit, harmless:
|
||||
discard
|
||||
of nkReturnStmt:
|
||||
for i in 0..<n.len:
|
||||
if containsResult(n[i]): return true
|
||||
result = n.len > 0 and n[0].kind == nkEmpty
|
||||
of nkSym:
|
||||
if n.sym.kind == skResult:
|
||||
result = true
|
||||
@@ -942,10 +1012,6 @@ proc containsResult(n: PNode): bool =
|
||||
for i in 0..<n.len:
|
||||
if containsResult(n[i]): return true
|
||||
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
|
||||
nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
|
||||
declarativeDefs
|
||||
|
||||
proc easyResultAsgn(n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
@@ -965,7 +1031,7 @@ proc easyResultAsgn(n: PNode): PNode =
|
||||
type
|
||||
InitResultEnum = enum Unknown, InitSkippable, InitRequired
|
||||
|
||||
proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
|
||||
# Exceptions coming from calls don't have not be considered here:
|
||||
#
|
||||
# proc bar(): string = raise newException(...)
|
||||
@@ -980,7 +1046,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
# echo "a was not written to"
|
||||
#
|
||||
template allPathsInBranch(it) =
|
||||
let a = allPathsAsgnResult(it)
|
||||
let a = allPathsAsgnResult(p, it)
|
||||
case a
|
||||
of InitRequired: return InitRequired
|
||||
of InitSkippable: discard
|
||||
@@ -992,7 +1058,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for it in n:
|
||||
result = allPathsAsgnResult(it)
|
||||
result = allPathsAsgnResult(p, it)
|
||||
if result != Unknown: return result
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
if n[0].kind == nkSym and n[0].sym.kind == skResult:
|
||||
@@ -1000,6 +1066,8 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
else: result = InitRequired
|
||||
elif containsResult(n):
|
||||
result = InitRequired
|
||||
else:
|
||||
result = allPathsAsgnResult(p, n[1])
|
||||
of nkReturnStmt:
|
||||
if n.len > 0:
|
||||
if n[0].kind == nkEmpty and result != InitSkippable:
|
||||
@@ -1008,7 +1076,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
# initialized. This avoids cases like #9286 where this heuristic lead to
|
||||
# wrong code being generated.
|
||||
result = InitRequired
|
||||
else: result = allPathsAsgnResult(n[0])
|
||||
else: result = allPathsAsgnResult(p, n[0])
|
||||
of nkIfStmt, nkIfExpr:
|
||||
var exhaustive = false
|
||||
result = InitSkippable
|
||||
@@ -1034,9 +1102,9 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
of nkWhileStmt:
|
||||
# some dubious code can assign the result in the 'while'
|
||||
# condition and that would be fine. Everything else isn't:
|
||||
result = allPathsAsgnResult(n[0])
|
||||
result = allPathsAsgnResult(p, n[0])
|
||||
if result == Unknown:
|
||||
result = allPathsAsgnResult(n[1])
|
||||
result = allPathsAsgnResult(p, n[1])
|
||||
# we cannot assume that the 'while' loop is really executed at least once:
|
||||
if result == InitSkippable: result = Unknown
|
||||
of harmless:
|
||||
@@ -1061,9 +1129,17 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
|
||||
allPathsInBranch(n[0])
|
||||
for i in 1..<n.len:
|
||||
if n[i].kind == nkFinally:
|
||||
result = allPathsAsgnResult(n[i].lastSon)
|
||||
result = allPathsAsgnResult(p, n[i].lastSon)
|
||||
else:
|
||||
allPathsInBranch(n[i].lastSon)
|
||||
of nkCallKinds:
|
||||
if canRaiseDisp(p, n[0]):
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
of nkRaiseStmt:
|
||||
result = InitRequired
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
allPathsInBranch(n[i])
|
||||
@@ -1078,7 +1154,7 @@ proc getProcTypeCast(m: BModule, prc: PSym): Rope =
|
||||
|
||||
proc genProcBody(p: BProc; procBody: PNode) =
|
||||
genStmts(p, procBody) # modifies p.locals, p.init, etc.
|
||||
if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}:
|
||||
if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}:
|
||||
p.flags.incl nimErrorFlagDeclared
|
||||
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
|
||||
p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
|
||||
@@ -1086,10 +1162,13 @@ proc genProcBody(p: BProc; procBody: PNode) =
|
||||
proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
|
||||
sfNoReturn in s.flags and m.config.exc != excGoto
|
||||
|
||||
proc genProcAux(m: BModule, prc: PSym) =
|
||||
proc genProcAux*(m: BModule, prc: PSym) =
|
||||
var p = newProc(prc, m)
|
||||
var header = newRopeAppender()
|
||||
genProcHeader(m, prc, header)
|
||||
if m.config.backend == backendCpp and {sfVirtual, sfConstructor} * prc.flags != {}:
|
||||
genMemberProcHeader(m, prc, header)
|
||||
else:
|
||||
genProcHeader(m, prc, header)
|
||||
var returnStmt: Rope = ""
|
||||
assert(prc.ast != nil)
|
||||
|
||||
@@ -1097,12 +1176,15 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
if sfInjectDestructors in prc.flags:
|
||||
procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)
|
||||
|
||||
let tmpInfo = prc.info
|
||||
discard freshLineInfo(p, prc.info)
|
||||
|
||||
if sfPure notin prc.flags and prc.typ[0] != nil:
|
||||
if resultPos >= prc.ast.len:
|
||||
internalError(m.config, prc.info, "proc has no result symbol")
|
||||
let resNode = prc.ast[resultPos]
|
||||
let res = resNode.sym # get result symbol
|
||||
if not isInvalidReturnType(m.config, prc.typ):
|
||||
if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags:
|
||||
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
|
||||
if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
|
||||
var decl = localVarDecl(p, resNode)
|
||||
@@ -1113,8 +1195,16 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
# declare the result symbol:
|
||||
assignLocalVar(p, resNode)
|
||||
assert(res.loc.r != "")
|
||||
initLocalVar(p, res, immediateAsgn=false)
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
|
||||
allPathsAsgnResult(p, procBody) == InitSkippable:
|
||||
# In an ideal world the codegen could rely on injectdestructors doing its job properly
|
||||
# and then the analysis step would not be required.
|
||||
discard "result init optimized out"
|
||||
else:
|
||||
initLocalVar(p, res, immediateAsgn=false)
|
||||
returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
|
||||
elif sfConstructor in prc.flags:
|
||||
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
|
||||
else:
|
||||
fillResult(p.config, resNode, prc.typ)
|
||||
assignParam(p, res, prc.typ[0])
|
||||
@@ -1125,7 +1215,7 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
# global is either 'nil' or points to valid memory and so the RC operation
|
||||
# succeeds without touching not-initialized memory.
|
||||
if sfNoInit in prc.flags: discard
|
||||
elif allPathsAsgnResult(procBody) == InitSkippable: discard
|
||||
elif allPathsAsgnResult(p, procBody) == InitSkippable: discard
|
||||
else:
|
||||
resetLoc(p, res.loc)
|
||||
if skipTypes(res.typ, abstractInst).kind == tyArray:
|
||||
@@ -1139,6 +1229,8 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||
closureSetup(p, prc)
|
||||
genProcBody(p, procBody)
|
||||
|
||||
prc.info = tmpInfo
|
||||
|
||||
var generatedProc: Rope
|
||||
generatedProc.genCLineDir prc.info, m.config
|
||||
if isNoReturn(p.module, prc):
|
||||
@@ -1190,7 +1282,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
|
||||
|
||||
proc genProcPrototype(m: BModule, sym: PSym) =
|
||||
useHeader(m, sym)
|
||||
if lfNoDecl in sym.loc.flags: return
|
||||
if lfNoDecl in sym.loc.flags or {sfVirtual, sfConstructor} * sym.flags != {}: return
|
||||
if lfDynamicLib in sym.loc.flags:
|
||||
if sym.itemId.module != m.module.position and
|
||||
not containsOrIncl(m.declaredThings, sym.id):
|
||||
@@ -1226,6 +1318,20 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
if lfNoDecl in prc.loc.flags:
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
genProcPrototype(m, prc)
|
||||
elif lfDynamicLib in prc.loc.flags:
|
||||
var q = findPendingModule(m, prc)
|
||||
fillProcLoc(q, prc.ast[namePos])
|
||||
genProcPrototype(m, prc)
|
||||
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
|
||||
symInDynamicLib(q, prc)
|
||||
# register the procedure even though it is in a different dynamic library and will not be
|
||||
# reloadable (and has no _actual suffix) - other modules will need to be able to get it through
|
||||
# the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
|
||||
if isReloadable(q, prc):
|
||||
q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
|
||||
[prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
|
||||
else:
|
||||
symInDynamicLibPartial(m, prc)
|
||||
elif prc.typ.callConv == ccInline:
|
||||
# We add inline procs to the calling module to enable C based inlining.
|
||||
# This also means that a check with ``q.declaredThings`` is wrong, we need
|
||||
@@ -1244,20 +1350,6 @@ proc genProcNoForward(m: BModule, prc: PSym) =
|
||||
# prc.loc.r = mangleName(m, prc)
|
||||
genProcPrototype(m, prc)
|
||||
genProcAux(m, prc)
|
||||
elif lfDynamicLib in prc.loc.flags:
|
||||
var q = findPendingModule(m, prc)
|
||||
fillProcLoc(q, prc.ast[namePos])
|
||||
genProcPrototype(m, prc)
|
||||
if q != nil and not containsOrIncl(q.declaredThings, prc.id):
|
||||
symInDynamicLib(q, prc)
|
||||
# register the procedure even though it is in a different dynamic library and will not be
|
||||
# reloadable (and has no _actual suffix) - other modules will need to be able to get it through
|
||||
# the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
|
||||
if isReloadable(q, prc):
|
||||
q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
|
||||
[prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
|
||||
else:
|
||||
symInDynamicLibPartial(m, prc)
|
||||
elif sfImportc notin prc.flags:
|
||||
var q = findPendingModule(m, prc)
|
||||
fillProcLoc(q, prc.ast[namePos])
|
||||
@@ -1321,14 +1413,14 @@ proc genVarPrototype(m: BModule, n: PNode) =
|
||||
if sym.owner.id != m.module.id:
|
||||
# else we already have the symbol generated!
|
||||
assert(sym.loc.r != "")
|
||||
incl(m.declaredThings, sym.id)
|
||||
if sfThread in sym.flags:
|
||||
declareThreadVar(m, sym, true)
|
||||
else:
|
||||
incl(m.declaredThings, sym.id)
|
||||
if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0:
|
||||
m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
|
||||
m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ")
|
||||
m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar))
|
||||
m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, dkVar))
|
||||
if m.hcrOn: m.s[cfsVars].add("*")
|
||||
if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
|
||||
if sfRegister in sym.flags: m.s[cfsVars].add(" register")
|
||||
@@ -1337,7 +1429,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
|
||||
m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
|
||||
if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
|
||||
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
|
||||
getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)])
|
||||
getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
|
||||
|
||||
proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
|
||||
result.addf("#define NIM_INTBITS $1\L", [
|
||||
@@ -1413,20 +1505,23 @@ proc genMainProc(m: BModule) =
|
||||
[handle, strLit])
|
||||
|
||||
preMainCode.add(loadLib("hcr_handle", "hcrGetProc"))
|
||||
preMainCode.add("\tvoid* rtl_handle;\L")
|
||||
preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
|
||||
preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
|
||||
preMainCode.add("\tinner = PreMain;\L")
|
||||
preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
|
||||
preMainCode.add("\t(*inner)();\L")
|
||||
if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
|
||||
else:
|
||||
preMainCode.add("\tvoid* rtl_handle;\L")
|
||||
preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
|
||||
preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
|
||||
preMainCode.add("\tinner = $1PreMain;\L" % [rope m.config.nimMainPrefix])
|
||||
preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
|
||||
preMainCode.add("\t(*inner)();\L")
|
||||
else:
|
||||
preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
|
||||
|
||||
var posixCmdLine: Rope
|
||||
if optNoMain notin m.config.globalOptions:
|
||||
posixCmdLine.add "\tN_LIB_PRIVATE int cmdCount;\L"
|
||||
posixCmdLine.add "\tN_LIB_PRIVATE char** cmdLine;\L"
|
||||
posixCmdLine.add "\tN_LIB_PRIVATE char** gEnv;\L"
|
||||
posixCmdLine.add "N_LIB_PRIVATE int cmdCount;\L"
|
||||
posixCmdLine.add "N_LIB_PRIVATE char** cmdLine;\L"
|
||||
posixCmdLine.add "N_LIB_PRIVATE char** gEnv;\L"
|
||||
|
||||
const
|
||||
# The use of a volatile function pointer to call Pre/NimMainInner
|
||||
@@ -1439,15 +1534,15 @@ proc genMainProc(m: BModule) =
|
||||
"}$N$N" &
|
||||
"$4" &
|
||||
"N_LIB_PRIVATE void $3PreMain(void) {$N" &
|
||||
"\t##if $5$N" & # 1 for volatile call, 0 for non-volatile
|
||||
"##if $5$N" & # 1 for volatile call, 0 for non-volatile
|
||||
"\tvoid (*volatile inner)(void);$N" &
|
||||
"\tinner = $3PreMainInner;$N" &
|
||||
"$1" &
|
||||
"\t(*inner)();$N" &
|
||||
"\t##else$N" &
|
||||
"##else$N" &
|
||||
"$1" &
|
||||
"\t$3PreMainInner();$N" &
|
||||
"\t##endif$N" &
|
||||
"##endif$N" &
|
||||
"}$N$N"
|
||||
|
||||
MainProcs =
|
||||
@@ -1462,17 +1557,17 @@ proc genMainProc(m: BModule) =
|
||||
|
||||
NimMainProc =
|
||||
"N_CDECL(void, $5NimMain)(void) {$N" &
|
||||
"\t##if $6$N" & # 1 for volatile call, 0 for non-volatile
|
||||
"##if $6$N" & # 1 for volatile call, 0 for non-volatile
|
||||
"\tvoid (*volatile inner)(void);$N" &
|
||||
"$4" &
|
||||
"\tinner = $5NimMainInner;$N" &
|
||||
"$2" &
|
||||
"\t(*inner)();$N" &
|
||||
"\t##else$N" &
|
||||
"##else$N" &
|
||||
"$4" &
|
||||
"$2" &
|
||||
"\t$5NimMainInner();$N" &
|
||||
"\t##endif$N" &
|
||||
"##endif$N" &
|
||||
"}$N$N"
|
||||
|
||||
NimMainBody = NimMainInner & NimMainProc
|
||||
@@ -1503,7 +1598,7 @@ proc genMainProc(m: BModule) =
|
||||
WinCDllMain =
|
||||
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
|
||||
" LPVOID lpvReserved) {$N" &
|
||||
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
|
||||
"\tif (fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "\t}$N" &
|
||||
"\treturn 1;$N}$N$N"
|
||||
|
||||
PosixNimDllMain = WinNimDllMain
|
||||
@@ -1537,11 +1632,11 @@ proc genMainProc(m: BModule) =
|
||||
m.includeHeader("<libc/component.h>")
|
||||
|
||||
let initStackBottomCall =
|
||||
if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope
|
||||
if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".rope
|
||||
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
|
||||
inc(m.labels)
|
||||
|
||||
let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: "1" else: "0"
|
||||
let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0"
|
||||
appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile])
|
||||
|
||||
if m.config.target.targetOS == osWindows and
|
||||
@@ -1681,7 +1776,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||
if sfSystemModule in m.module.flags:
|
||||
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
||||
g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
|
||||
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
|
||||
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
|
||||
g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
|
||||
|
||||
if m.s[cfsInitProc].len > 0:
|
||||
@@ -1706,7 +1801,7 @@ proc genDatInitCode(m: BModule) =
|
||||
|
||||
# we don't want to break into such init code - could happen if a line
|
||||
# directive from a function written by the user spills after itself
|
||||
genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
|
||||
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
|
||||
|
||||
for i in cfsTypeInit1..cfsDynLibInit:
|
||||
if m.s[i].len != 0:
|
||||
@@ -1732,10 +1827,10 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
|
||||
prc.typ.sym = nil
|
||||
|
||||
if not containsOrIncl(m.declaredThings, prc.id):
|
||||
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)])
|
||||
m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)])
|
||||
|
||||
result = "\t$1 = ($2) $3($4, $5);$n" %
|
||||
[tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
|
||||
[tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
|
||||
|
||||
proc genInitCode(m: BModule) =
|
||||
## this function is called in cgenWriteModules after all modules are closed,
|
||||
@@ -1747,7 +1842,7 @@ proc genInitCode(m: BModule) =
|
||||
[rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
|
||||
# we don't want to break into such init code - could happen if a line
|
||||
# directive from a function written by the user spills after itself
|
||||
genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
|
||||
genCLineDir(prc, InvalidFileIdx, 999999, m.config)
|
||||
if m.typeNodes > 0:
|
||||
if m.hcrOn:
|
||||
appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
|
||||
@@ -1811,13 +1906,13 @@ proc genInitCode(m: BModule) =
|
||||
if beforeRetNeeded in m.initProc.flags:
|
||||
prc.add("\tBeforeRet_: ;\n")
|
||||
|
||||
if sfMainModule in m.module.flags and m.config.exc == excGoto:
|
||||
if m.config.exc == excGoto:
|
||||
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
|
||||
m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
|
||||
|
||||
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
|
||||
prc.add(deinitFrame(m.initProc))
|
||||
elif sfMainModule in m.module.flags and m.config.exc == excGoto:
|
||||
elif m.config.exc == excGoto:
|
||||
if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
|
||||
m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
|
||||
|
||||
@@ -1862,6 +1957,40 @@ proc genInitCode(m: BModule) =
|
||||
|
||||
registerModuleToMain(m.g, m)
|
||||
|
||||
proc postprocessCode(conf: ConfigRef, r: var Rope) =
|
||||
# find the first directive
|
||||
var f = r.find(postprocessDirStart)
|
||||
if f == -1:
|
||||
return
|
||||
|
||||
var
|
||||
nimlnDirLastF = ""
|
||||
|
||||
var res: Rope = r.substr(0, f - 1)
|
||||
while f != -1:
|
||||
var
|
||||
e = r.find(postprocessDirEnd, f + 1)
|
||||
dir = r.substr(f + 1, e - 1).split(postprocessDirSep)
|
||||
case dir[0]
|
||||
of "nimln":
|
||||
if dir[2] == nimlnDirLastF:
|
||||
res.add("nimln_(" & dir[1] & ");")
|
||||
else:
|
||||
res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");")
|
||||
nimlnDirLastF = dir[2]
|
||||
else:
|
||||
raiseAssert "unexpected postprocess directive"
|
||||
|
||||
# find the next directive
|
||||
f = r.find(postprocessDirStart, e + 1)
|
||||
# copy the code until the next directive
|
||||
if f != -1:
|
||||
res.add(r.substr(e + 1, f - 1))
|
||||
else:
|
||||
res.add(r.substr(e + 1))
|
||||
|
||||
r = res
|
||||
|
||||
proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||
var moduleIsEmpty = true
|
||||
|
||||
@@ -1874,8 +2003,6 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||
openNamespaceNim(m.config.cppCustomNamespace, result)
|
||||
if m.s[cfsFrameDefines].len > 0:
|
||||
result.add(m.s[cfsFrameDefines])
|
||||
else:
|
||||
result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n")
|
||||
|
||||
for i in cfsForwardTypes..cfsProcs:
|
||||
if m.s[i].len > 0:
|
||||
@@ -1892,9 +2019,17 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
|
||||
if m.config.cppCustomNamespace.len > 0:
|
||||
closeNamespaceNim(result)
|
||||
|
||||
if optLineDir in m.config.options:
|
||||
var srcFileDefs = ""
|
||||
for fi in 0..m.config.m.fileInfos.high:
|
||||
srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n")
|
||||
result = srcFileDefs & result
|
||||
|
||||
if moduleIsEmpty:
|
||||
result = ""
|
||||
|
||||
postprocessCode(m.config, result)
|
||||
|
||||
proc initProcOptions(m: BModule): TOptions =
|
||||
let opts = m.config.options
|
||||
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
|
||||
@@ -2094,7 +2229,7 @@ proc updateCachedModule(m: BModule) =
|
||||
cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
|
||||
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode =
|
||||
## Also called from IC.
|
||||
if sfMainModule in m.module.flags:
|
||||
# phase ordering problem here: We need to announce this
|
||||
@@ -2116,7 +2251,10 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
|
||||
|
||||
if m.hcrOn:
|
||||
# make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
|
||||
cgsym(m, "programResult")
|
||||
let sym = magicsys.getCompilerProc(m.g.graph, "programResult")
|
||||
# ignore when not available, could be a module imported early in `system`
|
||||
if sym != nil:
|
||||
cgsymImpl m, sym
|
||||
if m.inHcrInitGuard:
|
||||
endBlock(m.initProc)
|
||||
|
||||
@@ -2133,15 +2271,14 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
|
||||
cgsym(m, "rawWrite")
|
||||
|
||||
# raise dependencies on behalf of genMainProc
|
||||
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
|
||||
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
|
||||
cgsym(m, "initStackBottomWith")
|
||||
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
||||
cgsym(m, "initThreadVarsEmulation")
|
||||
|
||||
if m.g.forwardedProcs.len == 0:
|
||||
incl m.flags, objHasKidsValid
|
||||
let disp = generateMethodDispatchers(graph)
|
||||
for x in disp: genProcAux(m, x.sym)
|
||||
result = generateMethodDispatchers(graph, m.idgen)
|
||||
|
||||
let mm = m
|
||||
m.g.modulesClosed.add mm
|
||||
|
||||
@@ -135,6 +135,7 @@ type
|
||||
# unconditionally...
|
||||
# nimtvDeps is VERY hard to cache because it's
|
||||
# not a list of IDs nor can it be made to be one.
|
||||
mangledPrcs*: HashSet[string]
|
||||
|
||||
TCGen = object of PPassContext # represents a C source file
|
||||
s*: TCFileSections # sections of the C file
|
||||
@@ -148,7 +149,7 @@ type
|
||||
typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
|
||||
# would be ideal but for some reason enums
|
||||
# don't seem to get cached so it'd generate
|
||||
# 1 ABI check per occurence in code
|
||||
# 1 ABI check per occurrence in code
|
||||
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
|
||||
@@ -193,15 +194,17 @@ proc initBlock*(): TBlock =
|
||||
result.sections[i] = newRopeAppender()
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
new(result)
|
||||
result.prc = prc
|
||||
result.module = module
|
||||
result.options = if prc != nil: prc.options
|
||||
else: module.config.options
|
||||
result.blocks = @[initBlock()]
|
||||
result.nestedTryStmts = @[]
|
||||
result.finallySafePoints = @[]
|
||||
result.sigConflicts = initCountTable[string]()
|
||||
result = BProc(
|
||||
prc: prc,
|
||||
module: module,
|
||||
optionsStack: if module.initProc != nil: module.initProc.optionsStack
|
||||
else: @[],
|
||||
options: if prc != nil: prc.options
|
||||
else: module.config.options,
|
||||
blocks: @[initBlock()],
|
||||
sigConflicts: initCountTable[string]())
|
||||
if optQuirky in result.options:
|
||||
result.flags = {nimErrorFlagDisabled}
|
||||
|
||||
proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
|
||||
|
||||
@@ -95,7 +95,8 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
return No
|
||||
if result == Yes:
|
||||
# check for return type:
|
||||
if not sameTypeOrNil(a.typ[0], b.typ[0]):
|
||||
# ignore flags of return types; # bug #22673
|
||||
if not sameTypeOrNil(a.typ[0], b.typ[0], {IgnoreFlags}):
|
||||
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
|
||||
# infer 'auto' from the base to make it consistent:
|
||||
b.typ[0] = a.typ[0]
|
||||
@@ -113,7 +114,7 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||
s.ast[dispatcherPos] = dispatcher
|
||||
|
||||
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
var disp = copySym(s, nextSymId(idgen))
|
||||
var disp = copySym(s, idgen)
|
||||
incl(disp.flags, sfDispatcher)
|
||||
excl(disp.flags, sfExported)
|
||||
let old = disp.typ
|
||||
@@ -127,7 +128,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
disp.loc.r = ""
|
||||
if s.typ[0] != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
|
||||
else:
|
||||
# We've encountered a method prototype without a filled-in
|
||||
# resultPos slot. We put a placeholder in there that will
|
||||
@@ -213,7 +214,7 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||
a[j] = v
|
||||
if h == 1: break
|
||||
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
|
||||
var base = methods[0].ast[dispatcherPos].sym
|
||||
result = base
|
||||
var paramLen = base.typ.len
|
||||
@@ -272,7 +273,7 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PS
|
||||
nilchecks.flags.incl nfTransf # should not be further transformed
|
||||
result.ast[bodyPos] = nilchecks
|
||||
|
||||
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
||||
proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
for bucket in 0..<g.methods.len:
|
||||
var relevantCols = initIntSet()
|
||||
@@ -282,4 +283,4 @@ proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
||||
# if multi-methods are not enabled, we are interested only in the first field
|
||||
break
|
||||
sortBucket(g.methods[bucket].methods, relevantCols)
|
||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))
|
||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols, idgen))
|
||||
|
||||
@@ -183,7 +183,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
|
||||
ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
|
||||
|
||||
proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info)
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info)
|
||||
result.typ = typ
|
||||
assert(not typ.isNil)
|
||||
|
||||
@@ -855,7 +855,9 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkReturnStmt:
|
||||
# We're somewhere in try, transform to finally unrolling
|
||||
assert(ctx.nearestFinally != 0)
|
||||
if ctx.nearestFinally == 0:
|
||||
# return is within the finally
|
||||
return
|
||||
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
@@ -1348,7 +1350,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
|
||||
let idefs = copyNode(it)
|
||||
for v in 0..it.len-3:
|
||||
if it[v].kind == nkSym:
|
||||
let x = copySym(it[v].sym, nextSymId(c.idgen))
|
||||
let x = copySym(it[v].sym, c.idgen)
|
||||
c.tab[it[v].sym.id] = x
|
||||
idefs.add newSymNode(x)
|
||||
else:
|
||||
@@ -1389,7 +1391,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||
discard c.finallys.pop()
|
||||
|
||||
of nkWhileStmt, nkBlockStmt:
|
||||
if n.hasYields == false: return n
|
||||
if not n.hasYields: return n
|
||||
c.blocks.add((n, c.finallys.len))
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
@@ -1431,9 +1433,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
# Lambda lifting was not done yet. Use temporary :state sym, which will
|
||||
# be handled specially by lambda lifting. Local temp vars (if needed)
|
||||
# should follow the same logic.
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info)
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
|
||||
var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
|
||||
var n = preprocess(pc, n.toStmtList)
|
||||
#echo "transformed into ", n
|
||||
@@ -1466,9 +1468,10 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
result = ctx.transformStateAssignments(result)
|
||||
result = ctx.wrapIntoStateLoop(result)
|
||||
|
||||
# echo "TRANSFORM TO STATES: "
|
||||
# echo renderTree(result)
|
||||
when false:
|
||||
echo "TRANSFORM TO STATES: "
|
||||
echo renderTree(result)
|
||||
|
||||
# echo "exception table:"
|
||||
# for i, e in ctx.exceptionTable:
|
||||
# echo i, " -> ", e
|
||||
echo "exception table:"
|
||||
for i, e in ctx.exceptionTable:
|
||||
echo i, " -> ", e
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
|
||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest
|
||||
|
||||
import
|
||||
options, idents, nimconf, extccomp, commands, msgs,
|
||||
@@ -55,6 +55,11 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
|
||||
if conf.cmd == cmdNimscript:
|
||||
incl(conf.globalOptions, optWasNimscript)
|
||||
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
|
||||
# restores `conf.notes` after loading config files
|
||||
# because it has overwrites the notes when compiling the system module which
|
||||
# is a foreign module compared to the project
|
||||
if conf.cmd in cmdBackends:
|
||||
conf.notes = conf.mainPackageNotes
|
||||
|
||||
if not self.suggestMode:
|
||||
let scriptFile = conf.projectFull.changeFileExt("nims")
|
||||
|
||||
@@ -205,7 +205,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else: localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
else:
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
of "all": # other note groups would be easy to support via additional cases
|
||||
notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
|
||||
@@ -238,9 +242,9 @@ proc processCompile(conf: ConfigRef; filename: string) =
|
||||
extccomp.addExternalFileToCompile(conf, found)
|
||||
|
||||
const
|
||||
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
||||
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
|
||||
template warningOptionNoop(switch: string) =
|
||||
@@ -262,6 +266,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
of "go": result = conf.selectedGC == gcGo
|
||||
of "none": result = conf.selectedGC == gcNone
|
||||
of "stack", "regions": result = conf.selectedGC == gcRegions
|
||||
of "atomicarc": result = conf.selectedGC == gcAtomicArc
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "opt":
|
||||
case arg.normalize
|
||||
@@ -336,6 +341,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
|
||||
of "panics": result = contains(conf.globalOptions, optPanics)
|
||||
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
|
||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
||||
@@ -515,14 +521,7 @@ proc initOrcDefines*(conf: ConfigRef) =
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
|
||||
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
|
||||
if isOrc:
|
||||
conf.selectedGC = gcOrc
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
else:
|
||||
conf.selectedGC = gcArc
|
||||
defineSymbol(conf.symbols, "gcarc")
|
||||
|
||||
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
@@ -532,10 +531,11 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
|
||||
proc unregisterArcOrc(conf: ConfigRef) =
|
||||
proc unregisterArcOrc*(conf: ConfigRef) =
|
||||
undefSymbol(conf.symbols, "gcdestructors")
|
||||
undefSymbol(conf.symbols, "gcarc")
|
||||
undefSymbol(conf.symbols, "gcorc")
|
||||
undefSymbol(conf.symbols, "gcatomicarc")
|
||||
undefSymbol(conf.symbols, "nimSeqsV2")
|
||||
undefSymbol(conf.symbols, "nimV2")
|
||||
excl conf.globalOptions, optSeqDestructors
|
||||
@@ -561,9 +561,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
of "destructors", "arc":
|
||||
registerArcOrc(pass, conf, false)
|
||||
conf.selectedGC = gcArc
|
||||
defineSymbol(conf.symbols, "gcarc")
|
||||
registerArcOrc(pass, conf)
|
||||
of "orc":
|
||||
registerArcOrc(pass, conf, true)
|
||||
conf.selectedGC = gcOrc
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
registerArcOrc(pass, conf)
|
||||
of "atomicarc":
|
||||
conf.selectedGC = gcAtomicArc
|
||||
defineSymbol(conf.symbols, "gcatomicarc")
|
||||
registerArcOrc(pass, conf)
|
||||
of "hooks":
|
||||
conf.selectedGC = gcHooks
|
||||
defineSymbol(conf.symbols, "gchooks")
|
||||
@@ -610,6 +618,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
var path = processPath(conf, arg, info, notRelativeToProj=true)
|
||||
let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
|
||||
if not nimbleDir.isEmpty and pass == passPP:
|
||||
path = nimbleDir / RelativeDir"pkgs2"
|
||||
nimblePath(conf, path, info)
|
||||
path = nimbleDir / RelativeDir"pkgs"
|
||||
nimblePath(conf, path, info)
|
||||
of "nonimblepath", "nobabelpath":
|
||||
@@ -651,6 +661,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
|
||||
if backend == backendJs: # bug #21209
|
||||
conf.globalOptions.excl {optThreadAnalysis, optThreads}
|
||||
if optRun in conf.globalOptions:
|
||||
# for now, -r uses nodejs, so define nodejs
|
||||
defineSymbol(conf.symbols, "nodejs")
|
||||
conf.backend = backend
|
||||
of "doccmd": conf.docCmd = arg
|
||||
of "define", "d":
|
||||
@@ -798,6 +811,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
defineSymbol(conf.symbols, "dll")
|
||||
of "staticlib":
|
||||
incl(conf.globalOptions, optGenStaticLib)
|
||||
incl(conf.globalOptions, optNoMain)
|
||||
excl(conf.globalOptions, optGenGuiApp)
|
||||
defineSymbol(conf.symbols, "library")
|
||||
defineSymbol(conf.symbols, "staticlib")
|
||||
@@ -821,6 +835,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "header":
|
||||
if conf != nil: conf.headerFile = arg
|
||||
incl(conf.globalOptions, optGenIndex)
|
||||
of "nimbasepattern":
|
||||
if conf != nil: conf.nimbasePattern = arg
|
||||
of "index":
|
||||
case arg.normalize
|
||||
of "", "on": conf.globalOptions.incl {optGenIndex}
|
||||
@@ -861,6 +877,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
setTarget(conf.target, conf.target.targetOS, cpu)
|
||||
of "run", "r":
|
||||
processOnOffSwitchG(conf, {optRun}, arg, pass, info)
|
||||
if conf.backend == backendJs:
|
||||
# for now, -r uses nodejs, so define nodejs
|
||||
defineSymbol(conf.symbols, "nodejs")
|
||||
of "maxloopiterationsvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.maxLoopIterationsVM = parseInt(arg)
|
||||
@@ -875,7 +894,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "verbosity":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
let verbosity = parseInt(arg)
|
||||
if verbosity notin {0..3}:
|
||||
if verbosity notin 0..3:
|
||||
localError(conf, info, "invalid verbosity level: '$1'" % arg)
|
||||
conf.verbosity = verbosity
|
||||
var verb = NotesVerbosity[conf.verbosity]
|
||||
@@ -1065,29 +1084,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "expandarc":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.arcToExpand[arg] = "T"
|
||||
of "useversion":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
case arg
|
||||
of "1.0":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "0")
|
||||
# old behaviors go here:
|
||||
defineSymbol(conf.symbols, "nimOldRelativePathBehavior")
|
||||
undefSymbol(conf.symbols, "nimDoesntTrackDefects")
|
||||
ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect}
|
||||
conf.globalOptions.incl optNimV1Emulation
|
||||
of "1.2":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "2")
|
||||
conf.globalOptions.incl optNimV12Emulation
|
||||
of "1.6":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "6")
|
||||
conf.globalOptions.incl optNimV16Emulation
|
||||
else:
|
||||
localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
|
||||
# always be compatible with 1.x.100:
|
||||
defineSymbol(conf.symbols, "NimPatch", "100")
|
||||
of "benchmarkvm":
|
||||
processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
|
||||
of "profilevm":
|
||||
@@ -1101,6 +1097,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
|
||||
if optPanics in conf.globalOptions:
|
||||
defineSymbol(conf.symbols, "nimPanics")
|
||||
of "jsbigint64":
|
||||
processOnOffSwitchG(conf, {optJsBigInt64}, arg, pass, info)
|
||||
of "sourcemap": # xxx document in --fullhelp
|
||||
conf.globalOptions.incl optSourcemap
|
||||
conf.options.incl optLineDir
|
||||
|
||||
28
compiler/compiler.nimble
Normal file
28
compiler/compiler.nimble
Normal file
@@ -0,0 +1,28 @@
|
||||
include "../lib/system/compilation.nim"
|
||||
version = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
skipDirs = @["."]
|
||||
installDirs = @["compiler"]
|
||||
|
||||
import os
|
||||
|
||||
var compilerDir = ""
|
||||
|
||||
before install:
|
||||
rmDir("compiler")
|
||||
|
||||
let
|
||||
files = listFiles(".")
|
||||
dirs = listDirs(".")
|
||||
|
||||
mkDir("compiler")
|
||||
|
||||
for f in files:
|
||||
cpFile(f, "compiler" / f)
|
||||
|
||||
for d in dirs:
|
||||
cpDir(d, "compiler" / d)
|
||||
|
||||
requires "nim"
|
||||
@@ -27,7 +27,7 @@ const
|
||||
proc declareSelf(c: PContext; info: TLineInfo) =
|
||||
## Adds the magical 'Self' symbols to the current scope.
|
||||
let ow = getCurrOwner(c)
|
||||
let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info)
|
||||
let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
|
||||
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
|
||||
s.typ.flags.incl {tfUnresolved, tfPacked}
|
||||
s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow)
|
||||
@@ -62,7 +62,7 @@ proc semConceptDecl(c: PContext; n: PNode): PNode =
|
||||
result[i] = n[i]
|
||||
result[^1] = semConceptDecl(c, n[^1])
|
||||
of nkCommentStmt:
|
||||
discard
|
||||
result = n
|
||||
else:
|
||||
localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
|
||||
result = n
|
||||
@@ -306,13 +306,15 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
result = matchSyms(c, n, {skMethod}, m)
|
||||
of nkIteratorDef:
|
||||
result = matchSyms(c, n, {skIterator}, m)
|
||||
of nkCommentStmt:
|
||||
result = true
|
||||
else:
|
||||
# error was reported earlier.
|
||||
result = false
|
||||
|
||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool =
|
||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
|
||||
## 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
|
||||
## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
|
||||
## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
|
||||
|
||||
@@ -45,10 +45,8 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimNewTypedesc") # deadcode
|
||||
defineSymbol("nimrequiresnimframe") # deadcode
|
||||
defineSymbol("nimparsebiggestfloatmagic") # deadcode
|
||||
defineSymbol("nimalias") # deadcode
|
||||
defineSymbol("nimlocks") # deadcode
|
||||
defineSymbol("nimnode") # deadcode pending `nimnode` reference in opengl package
|
||||
# refs https://github.com/nim-lang/opengl/pull/79
|
||||
defineSymbol("nimnode") # deadcode
|
||||
defineSymbol("nimvarargstyped") # deadcode
|
||||
defineSymbol("nimtypedescfixed") # deadcode
|
||||
defineSymbol("nimKnowsNimvm") # deadcode
|
||||
@@ -147,10 +145,19 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasCallsitePragma")
|
||||
defineSymbol("nimHasAmbiguousEnumHint")
|
||||
|
||||
defineSymbol("nimHasWarnCastSizes")
|
||||
defineSymbol("nimHasWarnCastSizes") # deadcode
|
||||
defineSymbol("nimHasOutParams")
|
||||
defineSymbol("nimHasSystemRaisesDefect")
|
||||
defineSymbol("nimHasWarnUnnamedBreak")
|
||||
defineSymbol("nimHasGenericDefine")
|
||||
defineSymbol("nimHasDefineAliases")
|
||||
defineSymbol("nimHasWarnBareExcept")
|
||||
defineSymbol("nimHasDup")
|
||||
defineSymbol("nimHasChecksums")
|
||||
defineSymbol("nimHasSendable")
|
||||
defineSymbol("nimAllowNonVarDestructor")
|
||||
defineSymbol("nimHasQuirky")
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
|
||||
@@ -63,12 +63,16 @@ proc toNimblePath(s: string, isStdlib: bool): string =
|
||||
sub.add "/pkgs/"
|
||||
var start = s.find(sub)
|
||||
if start < 0:
|
||||
result = s
|
||||
else:
|
||||
start += sub.len
|
||||
start += skipUntil(s, '/', start)
|
||||
start += 1
|
||||
result = pkgPrefix & s[start..^1]
|
||||
sub[^1] = '2'
|
||||
sub.add '/'
|
||||
start = s.find(sub) # /pkgs2
|
||||
if start < 0:
|
||||
return s
|
||||
|
||||
start += sub.len
|
||||
start += skipUntil(s, '/', start)
|
||||
start += 1
|
||||
result = pkgPrefix & s[start..^1]
|
||||
|
||||
proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
|
||||
doAssert n.kind == nkSym, $n.kind
|
||||
|
||||
@@ -264,7 +264,8 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
||||
c.blocks[i].raiseFixups.add lab1
|
||||
else:
|
||||
var trailingFinales: seq[PNode]
|
||||
if c.inTryStmt > 0: #Ok, we are in a try, lets see which (if any) try's we break out from:
|
||||
if c.inTryStmt > 0:
|
||||
# Ok, we are in a try, lets see which (if any) try's we break out from:
|
||||
for b in countdown(c.blocks.high, i):
|
||||
if c.blocks[b].isTryBlock:
|
||||
trailingFinales.add c.blocks[b].finale
|
||||
@@ -385,7 +386,8 @@ proc genCall(c: var Con; n: PNode) =
|
||||
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
|
||||
genDef(c, n[i])
|
||||
# every call can potentially raise:
|
||||
if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]):
|
||||
if c.inTryStmt > 0 and canRaiseConservative(n[0]):
|
||||
inc c.interestingInstructions
|
||||
# we generate the instruction sequence:
|
||||
# fork lab1
|
||||
# goto exceptionHandler (except or finally)
|
||||
@@ -493,7 +495,7 @@ proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
|
||||
gen(c, body)
|
||||
if root.kind == skResult:
|
||||
genImplicitReturn(c)
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result = c.code # will move
|
||||
else:
|
||||
shallowCopy(result, c.code)
|
||||
|
||||
@@ -112,13 +112,16 @@ type
|
||||
proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst)
|
||||
proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str)
|
||||
|
||||
proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex =
|
||||
proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
|
||||
let invalid = rstast.FileIndex(-1)
|
||||
result = d.nimToRstFid.getOrDefault(info.fileIndex, default = invalid)
|
||||
result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
|
||||
if result == invalid:
|
||||
let fname = toFullPath(d.conf, info)
|
||||
let fname = toFullPath(d.conf, fileIndex)
|
||||
result = addFilename(d.sharedState, fname)
|
||||
d.nimToRstFid[info.fileIndex] = result
|
||||
d.nimToRstFid[fileIndex] = result
|
||||
|
||||
proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex =
|
||||
addRstFileIndex(d, info.fileIndex)
|
||||
|
||||
proc cmpDecimalsIgnoreCase(a, b: string): int =
|
||||
## For sorting with correct handling of cases like 'uint8' and 'uint16'.
|
||||
@@ -256,7 +259,16 @@ template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
|
||||
of mwUnusedImportdoc: k = warnRstUnusedImportdoc
|
||||
of mwRstStyle: k = warnRstStyle
|
||||
{.gcsafe.}:
|
||||
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
|
||||
let errorsAsWarnings = (roPreferMarkdown in d.sharedState.options) and
|
||||
not d.standaloneDoc # not tolerate errors in .rst/.md files
|
||||
if whichMsgClass(msgKind) == mcError and errorsAsWarnings:
|
||||
liMessage(conf, newLineInfo(conf, AbsoluteFile filename, line, col),
|
||||
k, arg, doNothing, instLoc(), ignoreError=true)
|
||||
# when our Markdown parser fails, we currently can only terminate the
|
||||
# parsing (and then we will return monospaced text instead of markup):
|
||||
raiseRecoverableError("")
|
||||
else:
|
||||
globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
|
||||
|
||||
proc docgenFindFile(s: string): string {.gcsafe.} =
|
||||
result = options.findFile(conf, s).string
|
||||
@@ -308,8 +320,9 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
|
||||
standaloneDoc = false, preferMarkdown = true,
|
||||
hasToc = true): PDoc =
|
||||
let destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string
|
||||
declareClosures(currentFilename = filename, destFile = destFile)
|
||||
new(result)
|
||||
let d = result # pass `d` to `declareClosures`:
|
||||
declareClosures(currentFilename = filename, destFile = destFile)
|
||||
result.module = module
|
||||
result.conf = conf
|
||||
result.cache = cache
|
||||
@@ -415,16 +428,19 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||
proc genComment(d: PDoc, n: PNode): PRstNode =
|
||||
if n.comment.len > 0:
|
||||
d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
|
||||
result = parseRst(n.comment,
|
||||
toLinenumber(n.info),
|
||||
toColumn(n.info) + DocColOffset,
|
||||
d.conf, d.sharedState)
|
||||
try:
|
||||
result = parseRst(n.comment,
|
||||
toLinenumber(n.info),
|
||||
toColumn(n.info) + DocColOffset,
|
||||
d.conf, d.sharedState)
|
||||
except ERecoverableError:
|
||||
result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)])
|
||||
|
||||
proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
|
||||
if n == nil: return nil
|
||||
result = genComment(d, n)
|
||||
if result == nil:
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef,
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkTypeClassTy,
|
||||
nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}:
|
||||
# notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
|
||||
for i in 0..<n.len:
|
||||
@@ -562,10 +578,13 @@ proc runAllExamples(d: PDoc) =
|
||||
# most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
|
||||
# via command line
|
||||
# D20210224T221756:here
|
||||
let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [
|
||||
var pathArgs = "--path:$path" % [ "path", quoteShell(d.conf.projectPath) ]
|
||||
for p in d.conf.searchPaths:
|
||||
pathArgs = "$args --path:$path" % [ "args", pathArgs, "path", quoteShell(p) ]
|
||||
let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off $pathArgs --nimcache:$nimcache $rdoccmd $docCmd $file" % [
|
||||
"nim", quoteShell(os.getAppFilename()),
|
||||
"backend", $d.conf.backend,
|
||||
"path", quoteShell(d.conf.projectPath),
|
||||
"pathArgs", pathArgs,
|
||||
"libpath", quoteShell(d.conf.libpath),
|
||||
"nimcache", quoteShell(outputDir),
|
||||
"file", quoteShell(outp),
|
||||
@@ -600,7 +619,6 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
|
||||
let useRenderModule = false
|
||||
let loc = d.conf.toFileLineCol(n.info)
|
||||
let code = extractRunnableExamplesSource(d.conf, n)
|
||||
let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
|
||||
|
||||
if d.conf.errorCounter > 0:
|
||||
return (rdoccmd, code)
|
||||
@@ -616,6 +634,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
|
||||
docComment.comment = comment
|
||||
var runnableExamples = newTree(nkStmtList,
|
||||
docComment,
|
||||
newTree(nkImportStmt, newStrNode(nkStrLit, "std/assertions")),
|
||||
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
|
||||
runnableExamples.info = n.info
|
||||
for a in n.lastSon: runnableExamples.add a
|
||||
@@ -629,6 +648,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
|
||||
else:
|
||||
var code2 = code
|
||||
if code.len > 0 and "codeReordering" notin code:
|
||||
let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
|
||||
# hacky but simplest solution, until we devise a way to make `{.line.}`
|
||||
# work without introducing a scope
|
||||
code2 = """
|
||||
@@ -639,6 +659,7 @@ $#
|
||||
#[
|
||||
$#
|
||||
]#
|
||||
import std/assertions
|
||||
import $#
|
||||
$#
|
||||
""" % [comment, d.filename.quoted, code2]
|
||||
@@ -1060,7 +1081,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
|
||||
fileIndex: addRstFileIndex(d, nameNode.info))
|
||||
addAnchorNim(d.sharedState, external = false, refn = symbolOrId,
|
||||
tooltip = detailedName, langSym = rstLangSymbol,
|
||||
priority = symbolPriority(k), info = lineinfo)
|
||||
priority = symbolPriority(k), info = lineinfo,
|
||||
module = addRstFileIndex(d, FileIndex d.module.position))
|
||||
|
||||
let renderFlags =
|
||||
if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms,
|
||||
@@ -1123,13 +1145,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx
|
||||
if k == skType and nameNode.kind == nkSym:
|
||||
d.types.strTableAdd nameNode.sym
|
||||
|
||||
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem =
|
||||
proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem =
|
||||
if not isVisible(d, nameNode): return
|
||||
var
|
||||
name = getNameEsc(d, nameNode)
|
||||
comm = genRecComment(d, n)
|
||||
r: TSrcGen
|
||||
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing})
|
||||
renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}
|
||||
if nonExports:
|
||||
renderFlags.incl renderNonExportedFields
|
||||
initTokRender(r, n, renderFlags)
|
||||
result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
|
||||
"col": %n.info.col}
|
||||
if comm != nil:
|
||||
@@ -1451,7 +1476,8 @@ proc finishGenerateDoc*(d: var PDoc) =
|
||||
isGroup: true),
|
||||
priority = symbolPriority(k),
|
||||
# select index `0` just to have any meaningful warning:
|
||||
info = overloadChoices[0].info)
|
||||
info = overloadChoices[0].info,
|
||||
module = addRstFileIndex(d, FileIndex d.module.position))
|
||||
|
||||
if optGenIndexOnly in d.conf.globalOptions:
|
||||
return
|
||||
@@ -1513,7 +1539,7 @@ proc finishGenerateDoc*(d: var PDoc) =
|
||||
proc add(d: PDoc; j: JsonItem) =
|
||||
if j.json != nil or j.rst != nil: d.jEntriesPre.add j
|
||||
|
||||
proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) =
|
||||
proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool = true) =
|
||||
case n.kind
|
||||
of nkPragma:
|
||||
let doctypeNode = findPragma(n, wDoctype)
|
||||
@@ -1545,14 +1571,14 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) =
|
||||
if n[i].kind != nkCommentStmt:
|
||||
# order is always 'type var let const':
|
||||
d.add genJsonItem(d, n[i], n[i][0],
|
||||
succ(skType, ord(n.kind)-ord(nkTypeSection)))
|
||||
succ(skType, ord(n.kind)-ord(nkTypeSection)), optShowNonExportedFields in config.globalOptions)
|
||||
of nkStmtList:
|
||||
for i in 0..<n.len:
|
||||
generateJson(d, n[i], includeComments)
|
||||
generateJson(d, n[i], config, includeComments)
|
||||
of nkWhenStmt:
|
||||
# generate documentation for the first branch only:
|
||||
if not checkForFalse(n[0][0]):
|
||||
generateJson(d, lastSon(n[0]), includeComments)
|
||||
generateJson(d, lastSon(n[0]), config, includeComments)
|
||||
else: discard
|
||||
|
||||
proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
|
||||
@@ -1697,7 +1723,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string =
|
||||
"moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(),
|
||||
"content", content, "author", d.meta[metaAuthor],
|
||||
"version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics,
|
||||
"deprecationMsg", d.modDeprecationMsg]
|
||||
"deprecationMsg", d.modDeprecationMsg, "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
|
||||
else:
|
||||
code = content
|
||||
result = code
|
||||
@@ -1801,13 +1827,16 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
|
||||
var filen = addFileExt(filename, "txt")
|
||||
var d = newDocumentor(filen, cache, conf, outExt, standaloneDoc = true,
|
||||
preferMarkdown = preferMarkdown, hasToc = false)
|
||||
let rst = parseRst(readFile(filen.string),
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
conf, d.sharedState)
|
||||
d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
|
||||
finishGenerateDoc(d)
|
||||
writeOutput(d)
|
||||
generateIndex(d)
|
||||
try:
|
||||
let rst = parseRst(readFile(filen.string),
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
conf, d.sharedState)
|
||||
d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
|
||||
finishGenerateDoc(d)
|
||||
writeOutput(d)
|
||||
generateIndex(d)
|
||||
except ERecoverableError:
|
||||
discard "already reported the error"
|
||||
|
||||
proc commandRst2Html*(cache: IdentCache, conf: ConfigRef,
|
||||
preferMarkdown=false) =
|
||||
@@ -1826,7 +1855,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
|
||||
status: int; content: string) {.gcsafe.} =
|
||||
localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
|
||||
warnUser, "the ':test:' attribute is not supported by this backend")
|
||||
generateJson(d, ast)
|
||||
generateJson(d, ast, conf)
|
||||
finishGenerateDoc(d)
|
||||
let json = d.jEntriesFinal
|
||||
let content = pretty(json)
|
||||
@@ -1878,7 +1907,7 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"")
|
||||
"title", "Index",
|
||||
"subtitle", "", "tableofcontents", "", "moduledesc", "",
|
||||
"date", getDateStr(), "time", getClockStr(),
|
||||
"content", content, "author", "", "version", "", "analytics", ""]
|
||||
"content", content, "author", "", "version", "", "analytics", "", "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
|
||||
# no analytics because context is not available
|
||||
|
||||
try:
|
||||
|
||||
@@ -56,7 +56,7 @@ proc processNodeJson*(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
if shouldProcess(g):
|
||||
generateJson(g.doc, n, false)
|
||||
generateJson(g.doc, n, g.config, false)
|
||||
|
||||
template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
var g: PGen
|
||||
|
||||
@@ -6,12 +6,12 @@ when defined(nimPreviewSlimSystem):
|
||||
|
||||
|
||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
|
||||
dest.typ = t
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
res.typ = getSysType(g, info, tyString)
|
||||
|
||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
||||
@@ -67,12 +67,12 @@ proc searchObjCase(t: PType; field: PSym): PNode =
|
||||
doAssert result != nil
|
||||
|
||||
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
|
||||
dest.typ = field.typ
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
res.typ = getSysType(g, info, tyUInt8)
|
||||
|
||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## Template evaluation engine. Now hygienic.
|
||||
|
||||
import
|
||||
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
|
||||
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
|
||||
|
||||
type
|
||||
TemplCtx = object
|
||||
@@ -49,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
|
||||
var x = PSym(idTableGet(c.mapping, s))
|
||||
if x == nil:
|
||||
x = copySym(s, nextSymId(c.idgen))
|
||||
x = copySym(s, c.idgen)
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
x.owner = nil # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
|
||||
@@ -14,12 +14,14 @@
|
||||
|
||||
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
|
||||
|
||||
import std/[os, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
|
||||
import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
|
||||
|
||||
import std / strutils except addf
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
import std/[syncio, assertions]
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
type
|
||||
TInfoCCProp* = enum # properties of the C compiler:
|
||||
@@ -31,6 +33,7 @@ type
|
||||
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
|
||||
hasDeclspec, # CC has __declspec(X)
|
||||
hasAttribute, # CC has __attribute__((X))
|
||||
hasBuiltinUnreachable # CC has __builtin_unreachable
|
||||
TInfoCCProps* = set[TInfoCCProp]
|
||||
TInfoCC* = tuple[
|
||||
name: string, # the short name of the compiler
|
||||
@@ -93,7 +96,7 @@ compiler gcc:
|
||||
produceAsm: gnuAsmListing,
|
||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasAttribute})
|
||||
hasAttribute, hasBuiltinUnreachable})
|
||||
|
||||
# GNU C and C++ Compiler
|
||||
compiler nintendoSwitchGCC:
|
||||
@@ -120,7 +123,7 @@ compiler nintendoSwitchGCC:
|
||||
produceAsm: gnuAsmListing,
|
||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasAttribute})
|
||||
hasAttribute, hasBuiltinUnreachable})
|
||||
|
||||
# LLVM Frontend for GCC/G++
|
||||
compiler llvmGcc:
|
||||
@@ -578,7 +581,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||
|
||||
compilePattern = joinPath(conf.cCompilerPath, exe)
|
||||
else:
|
||||
compilePattern = getCompilerExe(conf, c, isCpp)
|
||||
compilePattern = exe
|
||||
|
||||
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
|
||||
|
||||
@@ -832,7 +835,10 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
|
||||
else:
|
||||
writeFile(linkerArgs, args)
|
||||
try:
|
||||
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
|
||||
when defined(macosx):
|
||||
execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs)
|
||||
else:
|
||||
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
|
||||
finally:
|
||||
removeFile(linkerArgs)
|
||||
|
||||
@@ -987,7 +993,7 @@ type BuildCache = object
|
||||
depfiles: seq[(string, string)]
|
||||
nimexe: string
|
||||
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
var linkFiles = collect(for it in conf.externalToLink:
|
||||
var it = it
|
||||
if conf.noAbsolutePaths: it = it.extractFilename
|
||||
@@ -1008,10 +1014,14 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
currentDir: getCurrentDir())
|
||||
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
|
||||
bcache.cmdline = conf.commandLine
|
||||
bcache.depfiles = collect(for it in conf.m.fileInfos:
|
||||
for it in conf.m.fileInfos:
|
||||
let path = it.fullPath.string
|
||||
if isAbsolute(path): # TODO: else?
|
||||
(path, $secureHashFile(path)))
|
||||
if path in deps:
|
||||
bcache.depfiles.add (path, deps[path])
|
||||
else: # backup for configs etc.
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
bcache.nimexe = hashNimExe()
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
|
||||
## Module that implements ``gorge`` for the compiler.
|
||||
|
||||
import msgs, std / sha1, os, osproc, streams, options,
|
||||
import msgs, os, osproc, streams, options,
|
||||
lineinfos, pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
proc readOutput(p: Process): (string, int) =
|
||||
result[0] = ""
|
||||
var output = p.outputStream
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode
|
||||
|
||||
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
|
||||
@@ -50,7 +50,10 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive
|
||||
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
|
||||
cgen.genTopLevelStmt(bmod, n)
|
||||
|
||||
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
let disps = finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
if disps != nil:
|
||||
for disp in disps:
|
||||
genProcAux(bmod, disp.sym)
|
||||
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
|
||||
|
||||
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
|
||||
|
||||
@@ -7,13 +7,15 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import hashes, tables, intsets, std/sha1
|
||||
import hashes, tables, intsets
|
||||
import packed_ast, bitabs, rodfiles
|
||||
import ".." / [ast, idents, lineinfos, msgs, ropes, options,
|
||||
pathutils, condsyms, packages, modulepaths]
|
||||
#import ".." / [renderer, astalgo]
|
||||
from os import removeFile, isAbsolute
|
||||
|
||||
import ../../dist/checksums/src/checksums/sha1
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, formatfloat]
|
||||
|
||||
@@ -372,7 +374,7 @@ proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib
|
||||
if l.isNil: return
|
||||
result.kind = l.kind
|
||||
result.generated = l.generated
|
||||
result.isOverriden = l.isOverriden
|
||||
result.isOverridden = l.isOverridden
|
||||
result.name = toLitId($l.name, m)
|
||||
storeNode(result, l, path)
|
||||
|
||||
@@ -390,7 +392,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
|
||||
assert sfForward notin s.flags
|
||||
|
||||
var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
|
||||
position: s.position, offset: s.offset, options: s.options,
|
||||
position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
|
||||
name: s.name.s.toLitId(m))
|
||||
|
||||
storeNode(p, s, ast)
|
||||
@@ -419,7 +421,7 @@ proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pac
|
||||
## add a remote symbol reference to the tree
|
||||
let info = n.info.toPackedInfo(c, m)
|
||||
ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total
|
||||
typeId: storeTypeLater(n.typ, c, m), info: info)
|
||||
typeId: storeTypeLater(n.typ, c, m), info: info, flags: n.flags)
|
||||
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
|
||||
operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
|
||||
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
|
||||
@@ -830,6 +832,7 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
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,
|
||||
disamb: s.disamb,
|
||||
name: getIdent(c.cache, g[si].fromDisk.strings[s.name])
|
||||
)
|
||||
|
||||
@@ -847,7 +850,7 @@ proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
if l.name.int == 0:
|
||||
result = nil
|
||||
else:
|
||||
result = PLib(generated: l.generated, isOverriden: l.isOverriden,
|
||||
result = PLib(generated: l.generated, isOverridden: l.isOverridden,
|
||||
kind: l.kind, name: rope g[si].fromDisk.strings[l.name])
|
||||
loadAstBody(l, path)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ type
|
||||
PackedLib* = object
|
||||
kind*: TLibKind
|
||||
generated*: bool
|
||||
isOverriden*: bool
|
||||
isOverridden*: bool
|
||||
name*: LitId
|
||||
path*: NodeId
|
||||
|
||||
@@ -63,7 +63,8 @@ type
|
||||
alignment*: int # for alignment
|
||||
options*: TOptions
|
||||
position*: int
|
||||
offset*: int
|
||||
offset*: int32
|
||||
disamb*: int32
|
||||
externalName*: LitId # instead of TLoc
|
||||
locFlags*: TLocFlags
|
||||
annex*: PackedLib
|
||||
|
||||
@@ -228,10 +228,15 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
|
||||
proc addUnique[T](x: var seq[T], y: sink T) {.noSideEffect.} =
|
||||
for i in 0..high(x):
|
||||
if x[i] == y: return
|
||||
x.add y
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
|
||||
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
|
||||
if n.kind != nkImportAs: discard
|
||||
elif n.len != 2 or n[1].kind != nkIdent:
|
||||
localError(c.config, n.info, "module alias must be an identifier")
|
||||
@@ -245,6 +250,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
|
||||
result.options.incl optImportHidden
|
||||
c.unusedImports.add((result, n.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
|
||||
var ret: typeof(result)
|
||||
@@ -298,7 +304,15 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
var prefix = ""
|
||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
||||
|
||||
proc suggestMod(n: PNode; s: PSym) =
|
||||
if n.kind == nkImportAs:
|
||||
suggestMod(n[0], realModule)
|
||||
elif n.kind == nkInfix:
|
||||
suggestMod(n[2], s)
|
||||
else:
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
suggestMod(n, result)
|
||||
importStmtResult.add newSymNode(result, n.info)
|
||||
#newStrNode(toFullPath(c.config, f), n.info)
|
||||
|
||||
@@ -323,22 +337,26 @@ proc evalImport*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkImportStmt, n.info)
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
|
||||
let sep = it[0]
|
||||
let dir = it[1]
|
||||
var imp = newNodeI(nkInfix, it.info)
|
||||
imp.add sep
|
||||
imp.add dir
|
||||
imp.add sep # dummy entry, replaced in the loop
|
||||
for x in it[2]:
|
||||
if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
|
||||
let lastPos = it.len - 1
|
||||
var imp = copyNode(it)
|
||||
newSons(imp, it.len)
|
||||
for i in 0 ..< lastPos: imp[i] = it[i]
|
||||
imp[lastPos] = imp[0] # dummy entry, replaced in the loop
|
||||
for x in it[lastPos]:
|
||||
# transform `a/b/[c as d]` to `/a/b/c as d`
|
||||
if x.kind == nkInfix and x[0].ident.s == "as":
|
||||
let impAs = copyTree(x)
|
||||
imp[2] = x[1]
|
||||
var impAs = copyNode(x)
|
||||
newSons(impAs, 3)
|
||||
impAs[0] = x[0]
|
||||
imp[lastPos] = x[1]
|
||||
impAs[1] = imp
|
||||
impMod(c, imp, result)
|
||||
impAs[2] = x[2]
|
||||
impAs.info = x[2].info
|
||||
impMod(c, impAs, result)
|
||||
else:
|
||||
imp[2] = x
|
||||
imp[lastPos] = x
|
||||
imp.info = x.info
|
||||
impMod(c, imp, result)
|
||||
else:
|
||||
impMod(c, it, result)
|
||||
|
||||
@@ -36,6 +36,7 @@ type
|
||||
body: PNode
|
||||
otherUsage: TLineInfo
|
||||
inUncheckedAssignSection: int
|
||||
inEnsureMove: int
|
||||
|
||||
Scope = object # we do scope-based memory management.
|
||||
# a scope is comparable to an nkStmtListExpr like
|
||||
@@ -66,11 +67,11 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
|
||||
result = ast.hasDestructor(t)
|
||||
when toDebug.len > 0:
|
||||
# for more effective debugging
|
||||
if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
|
||||
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
assert(not containsGarbageCollectedRef(t))
|
||||
|
||||
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
||||
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info)
|
||||
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
|
||||
sym.typ = typ
|
||||
s.vars.add(sym)
|
||||
result = newSymNode(sym)
|
||||
@@ -78,11 +79,11 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
|
||||
proc nestedScope(parent: var Scope; body: PNode): Scope =
|
||||
Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode
|
||||
|
||||
type
|
||||
MoveOrCopyFlag = enum
|
||||
IsDecl, IsExplicitSink
|
||||
IsDecl, IsExplicitSink, IsReturn
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
|
||||
|
||||
@@ -160,7 +161,8 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
|
||||
result = false
|
||||
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
if not hasDestructor(c, n.typ): return true
|
||||
# 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
|
||||
|
||||
let m = skipConvDfa(n)
|
||||
result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
|
||||
@@ -184,11 +186,16 @@ proc isCursor(n: PNode): bool =
|
||||
template isUnpackedTuple(n: PNode): bool =
|
||||
## we move out all elements of unpacked tuples,
|
||||
## hence unpacked tuples themselves don't need to be destroyed
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
|
||||
## except it's already a cursor
|
||||
(n.kind == nkSym and n.sym.kind == skTemp and
|
||||
n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
|
||||
|
||||
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
||||
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) =
|
||||
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
||||
if (opname == "=" or opname == "=copy") and ri != nil:
|
||||
if inferredFromCopy:
|
||||
m.add ", which is inferred from unavailable '=copy'"
|
||||
|
||||
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 '\''
|
||||
@@ -210,8 +217,12 @@ proc makePtrType(c: var Con, baseType: PType): PType =
|
||||
addSonSkipIntLit(result, baseType, c.idgen)
|
||||
|
||||
proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
|
||||
let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
|
||||
addrExp.add(dest)
|
||||
var addrExp: PNode
|
||||
if op.typ != nil and op.typ.len > 1 and op.typ[1].kind != tyVar:
|
||||
addrExp = dest
|
||||
else:
|
||||
addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
|
||||
addrExp.add(dest)
|
||||
result = newTree(nkCall, newSymNode(op), addrExp)
|
||||
|
||||
proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
|
||||
@@ -249,10 +260,20 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
|
||||
proc isNoInit(dest: PNode): bool {.inline.} =
|
||||
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
|
||||
|
||||
proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
proc deepAliases(dest, ri: PNode): bool =
|
||||
case ri.kind
|
||||
of nkCallKinds, nkStmtListExpr, nkBracket, nkTupleConstr, nkObjConstr,
|
||||
nkCast, nkConv, nkObjUpConv, nkObjDownConv:
|
||||
for r in ri:
|
||||
if deepAliases(dest, r): return true
|
||||
return false
|
||||
else:
|
||||
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
|
||||
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
|
||||
isNoInit(dest):
|
||||
isNoInit(dest) or IsReturn in flags:
|
||||
# optimize sink call into a bitwise memcopy
|
||||
result = newTree(nkFastAsgn, dest, ri)
|
||||
else:
|
||||
@@ -263,12 +284,19 @@ proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNod
|
||||
else:
|
||||
# the default is to use combination of `=destroy(dest)` and
|
||||
# and copyMem(dest, source). This is efficient.
|
||||
result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
|
||||
if deepAliases(dest, ri):
|
||||
# consider: x = x + y, it is wrong to destroy the destination first!
|
||||
# tmp to support self assignments
|
||||
let tmp = c.getTemp(s, dest.typ, dest.info)
|
||||
result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
|
||||
c.genDestroy(tmp))
|
||||
else:
|
||||
result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
|
||||
|
||||
proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
#[
|
||||
Lins's idea that only "critical" links can introduce a cycle. This is
|
||||
critical for the performance gurantees that we strive for: If you
|
||||
critical for the performance guarantees that we strive for: If you
|
||||
traverse a data structure, no tracing will be performed at all.
|
||||
ORC is about this promise: The GC only touches the memory that the
|
||||
mutator touches too.
|
||||
@@ -287,14 +315,14 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
|
||||
|
||||
proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(t):
|
||||
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(c.graph, t):
|
||||
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
|
||||
|
||||
proc genMarkCyclic(c: var Con; result, dest: PNode) =
|
||||
if c.graph.config.selectedGC == gcOrc:
|
||||
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if cyclicType(t):
|
||||
if cyclicType(c.graph, t):
|
||||
if t.kind == tyRef:
|
||||
result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
|
||||
else:
|
||||
@@ -308,6 +336,9 @@ proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
|
||||
assert ri.typ != nil
|
||||
|
||||
proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
|
||||
if c.inEnsureMove > 0:
|
||||
localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri &
|
||||
"', which introduces an implicit copy")
|
||||
let t = dest.typ
|
||||
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||
# try to improve the error message here:
|
||||
@@ -336,7 +367,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
|
||||
|
||||
if hasDestructor(c, objType):
|
||||
if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
|
||||
sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
|
||||
sfOverridden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
|
||||
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
|
||||
It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
|
||||
result.add newTree(nkFastAsgn, le, tmp)
|
||||
@@ -364,7 +395,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 & ")")
|
||||
@@ -376,13 +407,16 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
|
||||
proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
if not hasDestructor(c, n.typ):
|
||||
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
|
||||
if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0:
|
||||
assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or
|
||||
(n.typ.kind == tyPtr and n.sym.typ.kind == tyRef)
|
||||
# bug #23505; transformed by `transf`: addr (deref ref) -> ptr
|
||||
# we know it's really a pointer; so here we assign it directly
|
||||
result = copyTree(n)
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info)
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info)
|
||||
temp.typ = n.typ
|
||||
var v = newNodeI(nkLetSection, n.info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
@@ -395,7 +429,8 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
|
||||
result.add v
|
||||
let nn = skipConv(n)
|
||||
c.genMarkCyclic(result, nn)
|
||||
if hasDestructor(c, n.typ):
|
||||
c.genMarkCyclic(result, nn)
|
||||
let wasMovedCall = c.genWasMoved(nn)
|
||||
result.add wasMovedCall
|
||||
result.add tempAsNode
|
||||
@@ -406,21 +441,46 @@ proc isCapturedVar(n: PNode): bool =
|
||||
|
||||
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
let tmp = c.getTemp(s, n.typ, n.info)
|
||||
if hasDestructor(c, n.typ):
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
|
||||
let tmp = c.getTemp(s, nTyp, n.info)
|
||||
if hasDestructor(c, nTyp):
|
||||
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let op = getAttachedOp(c.graph, typ, attachedDup)
|
||||
if op != nil and tfHasOwned notin typ.flags:
|
||||
if sfError in op.flags:
|
||||
c.checkForErrorPragma(nTyp, n, "=dup")
|
||||
else:
|
||||
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
|
||||
if copyOp != nil and sfError in copyOp.flags and
|
||||
sfOverridden notin op.flags:
|
||||
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
|
||||
|
||||
let src = p(n, c, s, normal)
|
||||
var newCall = newTreeIT(nkCall, src.info, src.typ,
|
||||
newSymNode(op),
|
||||
src)
|
||||
c.finishCopy(newCall, n, isFromSink = true)
|
||||
result.add newTreeI(nkFastAsgn,
|
||||
src.info, tmp,
|
||||
newCall
|
||||
)
|
||||
else:
|
||||
result.add c.genWasMoved(tmp)
|
||||
var m = c.genCopy(tmp, n, {})
|
||||
m.add p(n, c, s, normal)
|
||||
c.finishCopy(m, n, isFromSink = true)
|
||||
result.add m
|
||||
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
|
||||
message(c.graph.config, n.info, hintPerformance,
|
||||
("passing '$1' to a sink parameter introduces an implicit copy; " &
|
||||
"if possible, rearrange your program's control flow to prevent it") % $n)
|
||||
if c.inEnsureMove > 0:
|
||||
localError(c.graph.config, n.info, errFailedMove,
|
||||
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
|
||||
else:
|
||||
if c.graph.config.selectedGC in {gcArc, gcOrc}:
|
||||
assert(not containsManagedMemory(n.typ))
|
||||
if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
assert(not containsManagedMemory(nTyp))
|
||||
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
|
||||
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
|
||||
# Since we know somebody will take over the produced copy, there is
|
||||
@@ -454,14 +514,14 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
|
||||
# This was already done in the sink parameter handling logic.
|
||||
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
|
||||
let tmp = c.getTemp(s, arg.typ, arg.info)
|
||||
result.add c.genSink(tmp, arg, {IsDecl})
|
||||
result.add c.genSink(s, tmp, arg, {IsDecl})
|
||||
result.add tmp
|
||||
s.final.add c.genDestroy(tmp)
|
||||
else:
|
||||
result = arg
|
||||
|
||||
proc cycleCheck(n: PNode; c: var Con) =
|
||||
if c.graph.config.selectedGC != gcArc: return
|
||||
if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return
|
||||
var value = n[1]
|
||||
if value.kind == nkClosure:
|
||||
value = value[1]
|
||||
@@ -534,7 +594,9 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
|
||||
# tricky because you would have to intercept moveOrCopy at a certain point
|
||||
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
|
||||
tmp.sym.flags = tmpFlags
|
||||
let cpy = if hasDestructor(c, ret.typ):
|
||||
let cpy = if hasDestructor(c, ret.typ) and
|
||||
ret.typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
# bug #23247 we don't own the data, so it's harmful to destroy it
|
||||
s.parent[].final.add c.genDestroy(tmp)
|
||||
moveOrCopy(tmp, ret, c, s, {IsDecl})
|
||||
else:
|
||||
@@ -662,6 +724,24 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
|
||||
of nkWhen: # This should be a "when nimvm" node.
|
||||
result = copyTree(n)
|
||||
result[1][0] = processCall(n[1][0], s)
|
||||
|
||||
of nkPragmaBlock:
|
||||
var inUncheckedAssignSection = 0
|
||||
let pragmaList = n[0]
|
||||
for pi in pragmaList:
|
||||
if whichPragma(pi) == wCast:
|
||||
case whichPragma(pi[1])
|
||||
of wUncheckedAssign:
|
||||
inUncheckedAssignSection = 1
|
||||
else:
|
||||
discard
|
||||
result = shallowCopy(n)
|
||||
inc c.inUncheckedAssignSection, inUncheckedAssignSection
|
||||
for i in 0 ..< n.len-1:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
result[^1] = maybeVoid(n[^1], s)
|
||||
dec c.inUncheckedAssignSection, inUncheckedAssignSection
|
||||
|
||||
else: assert(false)
|
||||
|
||||
proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
@@ -689,9 +769,21 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
|
||||
result.add copyNode(n[0])
|
||||
s.needsTry = true
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode =
|
||||
template isCustomDestructor(c: Con, t: PType): bool =
|
||||
hasDestructor(c, t) and
|
||||
getAttachedOp(c.graph, t, attachedDestructor) != nil and
|
||||
sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags
|
||||
|
||||
proc hasCustomDestructor(c: Con, t: PType): bool =
|
||||
result = isCustomDestructor(c, t)
|
||||
var obj = t
|
||||
while obj.len > 0 and obj[0] != nil:
|
||||
obj = skipTypes(obj[0], abstractPtrs)
|
||||
result = result or isCustomDestructor(c, obj)
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt}:
|
||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
|
||||
template process(child, s): untyped = p(child, c, s, mode)
|
||||
handleNestedTempl(n, process, tmpFlags = tmpFlags)
|
||||
elif mode == sinkArg:
|
||||
@@ -702,7 +794,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = passCopyToSink(n, c, s)
|
||||
elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
|
||||
nkCallKinds + nkLiterals:
|
||||
result = p(n, c, s, consumed)
|
||||
if n.kind in nkCallKinds and n[0].kind == nkSym:
|
||||
if n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
else:
|
||||
result = p(n, c, s, consumed)
|
||||
elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
|
||||
isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
|
||||
# Sinked params can be consumed only once. We need to reset the memory
|
||||
@@ -771,9 +871,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result[i][1] = p(n[i][1], c, s, m)
|
||||
else:
|
||||
result[i] = p(n[i], c, s, m)
|
||||
if mode == normal and isRefConstr:
|
||||
if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
|
||||
inc c.inEnsureMove
|
||||
result = p(n[1], c, s, sinkArg)
|
||||
dec c.inEnsureMove
|
||||
return
|
||||
|
||||
let inSpawn = c.inSpawn
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
|
||||
c.inSpawn.inc
|
||||
@@ -804,14 +910,17 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
|
||||
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
|
||||
result[0] = copyTree(n[0])
|
||||
if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
|
||||
if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}:
|
||||
let destroyOld = c.genDestroy(result[1])
|
||||
result = newTree(nkStmtList, destroyOld, result)
|
||||
else:
|
||||
result[0] = p(n[0], c, s, normal)
|
||||
if canRaise(n[0]): s.needsTry = true
|
||||
if mode == normal:
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
# Returns of openarray types shouldn't be destroyed
|
||||
# bug #19435; # bug #23247
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
of nkDiscardStmt: # Small optimization
|
||||
result = shallowCopy(n)
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -859,7 +968,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
|
||||
cycleCheck(n, c)
|
||||
assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
|
||||
let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
||||
var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
|
||||
if inReturn:
|
||||
flags.incl(IsReturn)
|
||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
|
||||
elif isDiscriminantField(n[0]):
|
||||
result = c.genDiscriminantAsgn(s, n)
|
||||
@@ -887,25 +998,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
if mode == normal:
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
|
||||
of nkPragmaBlock:
|
||||
var inUncheckedAssignSection = 0
|
||||
let pragmaList = n[0]
|
||||
for pi in pragmaList:
|
||||
if whichPragma(pi) == wCast:
|
||||
case whichPragma(pi[1])
|
||||
of wUncheckedAssign:
|
||||
inUncheckedAssignSection = 1
|
||||
else:
|
||||
discard
|
||||
result = shallowCopy(n)
|
||||
inc c.inUncheckedAssignSection, inUncheckedAssignSection
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = p(n[i], c, s, normal)
|
||||
dec c.inUncheckedAssignSection, inUncheckedAssignSection
|
||||
if n.typ != nil and hasDestructor(c, n.typ):
|
||||
if mode == normal:
|
||||
result = ensureDestruction(result, n, c, s)
|
||||
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv:
|
||||
# we have an "ownership invariance" for all constructors C(x).
|
||||
# See the comment for nkBracket construction. If the caller wants
|
||||
@@ -962,7 +1054,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
of nkReturnStmt:
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = p(n[i], c, s, mode)
|
||||
result[i] = p(n[i], c, s, mode, inReturn=true)
|
||||
s.needsTry = true
|
||||
of nkCast:
|
||||
result = shallowCopy(n)
|
||||
@@ -980,7 +1072,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
|
||||
proc sameLocation*(a, b: PNode): bool =
|
||||
proc sameConstant(a, b: PNode): bool =
|
||||
a.kind in nkLiterals and a.intVal == b.intVal
|
||||
a.kind in nkLiterals and b.kind in nkLiterals and a.intVal == b.intVal
|
||||
|
||||
const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
|
||||
if a.kind in nkEndPoint and b.kind in nkEndPoint:
|
||||
@@ -1004,9 +1096,9 @@ proc sameLocation*(a, b: PNode): bool =
|
||||
of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
|
||||
else: false
|
||||
|
||||
proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
# with side effects
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
|
||||
var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info)
|
||||
temp.typ = ri[1].typ
|
||||
var v = newNodeI(nkLetSection, ri[1].info)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
@@ -1021,14 +1113,22 @@ proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCop
|
||||
newAccess.add ri[0]
|
||||
newAccess.add tempAsNode
|
||||
|
||||
var snk = c.genSink(dest, newAccess, flags)
|
||||
var snk = c.genSink(s, dest, newAccess, flags)
|
||||
result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
|
||||
|
||||
proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
|
||||
var ri = ri
|
||||
var isEnsureMove = 0
|
||||
if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mEnsureMove:
|
||||
ri = ri[1]
|
||||
isEnsureMove = 1
|
||||
if sameLocation(dest, ri):
|
||||
# rule (self-assignment-removal):
|
||||
result = newNodeI(nkEmpty, dest.info)
|
||||
elif isCursor(dest):
|
||||
elif isCursor(dest) or dest.typ.kind in {tyOpenArray, tyVarargs}:
|
||||
# hoisted openArray parameters might end up here
|
||||
# openArray types don't have a lifted assignment operation (it's empty)
|
||||
# bug #22132
|
||||
case ri.kind:
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
|
||||
@@ -1037,53 +1137,60 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
else:
|
||||
result = newTree(nkFastAsgn, dest, p(ri, c, s, normal))
|
||||
else:
|
||||
case ri.kind
|
||||
let ri2 = if ri.kind == nkWhen: ri[1][0] else: ri
|
||||
case ri2.kind
|
||||
of nkCallKinds:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkBracketExpr:
|
||||
if isUnpackedTuple(ri[0]):
|
||||
# unpacking of tuple: take over the elements
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
|
||||
if aliases(dest, ri) == no:
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
if isAtom(ri[1]):
|
||||
var snk = c.genSink(dest, ri, flags)
|
||||
var snk = c.genSink(s, dest, ri, flags)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
result = genFieldAccessSideEffects(c, dest, ri, flags)
|
||||
result = genFieldAccessSideEffects(c, s, dest, ri, flags)
|
||||
else:
|
||||
result = c.genSink(dest, destructiveMoveVar(ri, c, s), flags)
|
||||
result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags)
|
||||
else:
|
||||
inc c.inEnsureMove, isEnsureMove
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
inc c.inEnsureMove, isEnsureMove
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
else:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
|
||||
result = c.genSink(dest, p(ri, c, s, consumed), flags)
|
||||
result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
|
||||
of nkSym:
|
||||
if isSinkParam(ri.sym) and isLastRead(ri, c, s):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, flags)
|
||||
let snk = c.genSink(s, dest, ri, flags)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
||||
isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
let snk = c.genSink(dest, ri, flags)
|
||||
let snk = c.genSink(s, dest, ri, flags)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
inc c.inEnsureMove, isEnsureMove
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
|
||||
result = c.genSink(dest, p(ri, c, s, sinkArg), flags)
|
||||
result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
|
||||
of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
|
||||
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
|
||||
@@ -1094,10 +1201,12 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy
|
||||
if 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(dest, ri, flags)
|
||||
let snk = c.genSink(s, dest, ri, flags)
|
||||
result = newTree(nkStmtList, snk, c.genWasMoved(ri))
|
||||
else:
|
||||
inc c.inEnsureMove, isEnsureMove
|
||||
result = c.genCopy(dest, ri, flags)
|
||||
dec c.inEnsureMove, isEnsureMove
|
||||
result.add p(ri, c, s, consumed)
|
||||
c.finishCopy(result, dest, isFromSink = false)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Platforms: """
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
netbsd: i386;amd64
|
||||
netbsd: i386;amd64;arm64
|
||||
openbsd: i386;amd64;arm;arm64
|
||||
dragonfly: i386;amd64
|
||||
crossos: amd64
|
||||
@@ -78,6 +78,7 @@ Files: "lib"
|
||||
[Other]
|
||||
Files: "examples"
|
||||
Files: "dist/nimble"
|
||||
Files: "dist/checksums"
|
||||
|
||||
Files: "tests"
|
||||
|
||||
@@ -91,6 +92,7 @@ Files: "bin/nimgrab.exe"
|
||||
Files: "bin/nimpretty.exe"
|
||||
Files: "bin/testament.exe"
|
||||
Files: "bin/nim-gdb.bat"
|
||||
Files: "bin/atlas.exe"
|
||||
|
||||
Files: "koch.exe"
|
||||
Files: "finish.exe"
|
||||
|
||||
@@ -72,6 +72,69 @@ proc isValueOnlyType(t: PType): bool =
|
||||
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
|
||||
result = not types.searchTypeFor(t, wrap)
|
||||
|
||||
type
|
||||
SearchResult = enum
|
||||
NotFound, Abort, Found
|
||||
|
||||
proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult
|
||||
|
||||
proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult =
|
||||
result = NotFound
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
result = containsDangerousRefAux(n[i], marker)
|
||||
if result == Found: return result
|
||||
of nkRecCase:
|
||||
assert(n[0].kind == nkSym)
|
||||
result = containsDangerousRefAux(n[0], marker)
|
||||
if result == Found: return result
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = containsDangerousRefAux(lastSon(n[i]), marker)
|
||||
if result == Found: return result
|
||||
else: discard
|
||||
of nkSym:
|
||||
result = containsDangerousRefAux(n.sym.typ, marker)
|
||||
else: discard
|
||||
|
||||
proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
|
||||
result = NotFound
|
||||
if t == nil: return result
|
||||
if containsOrIncl(marker, t.id): return result
|
||||
|
||||
if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure):
|
||||
result = Found
|
||||
elif tfSendable in t.flags:
|
||||
result = Abort
|
||||
else:
|
||||
# continue the type traversal:
|
||||
result = NotFound
|
||||
|
||||
if result != NotFound: return result
|
||||
case t.kind
|
||||
of tyObject:
|
||||
if t[0] != nil:
|
||||
result = containsDangerousRefAux(t[0].skipTypes(skipPtrs), marker)
|
||||
if result == NotFound: result = containsDangerousRefAux(t.n, marker)
|
||||
of tyGenericInst, tyDistinct, tyAlias, tySink:
|
||||
result = containsDangerousRefAux(lastSon(t), marker)
|
||||
of tyArray, tySet, tyTuple, tySequence:
|
||||
for i in 0..<t.len:
|
||||
result = containsDangerousRefAux(t[i], marker)
|
||||
if result == Found: return result
|
||||
else:
|
||||
discard
|
||||
|
||||
proc containsDangerousRef(t: PType): bool =
|
||||
# a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`.
|
||||
# For example:
|
||||
# `ref int` # dangerous
|
||||
# `Isolated[ref int]` # not dangerous
|
||||
var marker = initIntSet()
|
||||
result = containsDangerousRefAux(t, marker) == Found
|
||||
|
||||
proc canAlias*(arg, ret: PType): bool =
|
||||
if isValueOnlyType(arg):
|
||||
# can alias only with addr(arg.x) and we don't care if it is not safe
|
||||
@@ -80,12 +143,15 @@ proc canAlias*(arg, ret: PType): bool =
|
||||
var marker = initIntSet()
|
||||
result = canAlias(arg, ret, marker)
|
||||
|
||||
const
|
||||
SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
|
||||
|
||||
proc containsVariable(n: PNode): bool =
|
||||
case n.kind
|
||||
of nodesToIgnoreSet:
|
||||
result = false
|
||||
of nkSym:
|
||||
result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
|
||||
result = n.sym.kind in SomeVar
|
||||
else:
|
||||
for ch in n:
|
||||
if containsVariable(ch): return true
|
||||
@@ -109,7 +175,7 @@ proc checkIsolate*(n: PNode): bool =
|
||||
discard "fine, it is isolated already"
|
||||
else:
|
||||
let argType = n[i].typ
|
||||
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
|
||||
if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
|
||||
if argType.canAlias(n.typ) or containsVariable(n[i]):
|
||||
# bug #19013: Alias information is not enough, we need to check for potential
|
||||
# "overlaps". I claim the problem can only happen by reading again from a location
|
||||
@@ -143,6 +209,12 @@ proc checkIsolate*(n: PNode): bool =
|
||||
result = checkIsolate(n[^1])
|
||||
else:
|
||||
result = false
|
||||
of nkSym:
|
||||
result = true
|
||||
if n.sym.kind in SomeVar:
|
||||
let argType = n.typ
|
||||
if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
|
||||
result = false
|
||||
else:
|
||||
# unanalysable expression:
|
||||
result = false
|
||||
|
||||
@@ -50,6 +50,7 @@ type
|
||||
graph: ModuleGraph
|
||||
config: ConfigRef
|
||||
sigConflicts: CountTable[SigHash]
|
||||
initProc: PProc
|
||||
|
||||
BModule = ref TJSGen
|
||||
TJSTypeKind = enum # necessary JS "types"
|
||||
@@ -110,6 +111,7 @@ type
|
||||
extraIndent: int
|
||||
up: PProc # up the call chain; required for closure support
|
||||
declaredGlobals: IntSet
|
||||
previousFileName: string # For frameInfo inside templates.
|
||||
|
||||
template config*(p: PProc): ConfigRef = p.module.config
|
||||
|
||||
@@ -157,6 +159,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
|
||||
options: TOptions): PProc =
|
||||
result = PProc(
|
||||
blocks: @[],
|
||||
optionsStack: if module.initProc != nil: module.initProc.optionsStack
|
||||
else: @[],
|
||||
options: options,
|
||||
module: module,
|
||||
procDef: procDef,
|
||||
@@ -555,28 +559,36 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
|
||||
r.res = frmt % [a, b, tmp, tmp2]
|
||||
r.kind = resExpr
|
||||
|
||||
proc unsignedTrimmerJS(size: BiggestInt): Rope =
|
||||
proc unsignedTrimmer(size: BiggestInt): string =
|
||||
case size
|
||||
of 1: rope"& 0xff"
|
||||
of 2: rope"& 0xffff"
|
||||
of 4: rope">>> 0"
|
||||
else: rope""
|
||||
of 1: "& 0xff"
|
||||
of 2: "& 0xffff"
|
||||
of 4: ">>> 0"
|
||||
else: ""
|
||||
|
||||
|
||||
template unsignedTrimmer(size: BiggestInt): Rope =
|
||||
size.unsignedTrimmerJS
|
||||
proc signedTrimmer(size: BiggestInt): string =
|
||||
# sign extension is done by shifting to the left and then back to the right
|
||||
"<< $1 >> $1" % [$(32 - size * 8)]
|
||||
|
||||
proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
|
||||
reassign: static[bool] = false) =
|
||||
var x, y: TCompRes
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
|
||||
let size = n[1].typ.skipTypes(abstractRange).size
|
||||
when reassign:
|
||||
let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
|
||||
r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
|
||||
if size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "$1 = BigInt.asUintN(64, ($4 $2 $3))" % [a, rope op, y.rdLoc, tmp]
|
||||
else:
|
||||
let trimmer = unsignedTrimmer(size)
|
||||
r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
|
||||
else:
|
||||
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
|
||||
if size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "BigInt.asUintN(64, ($1 $2 $3))" % [x.rdLoc, rope op, y.rdLoc]
|
||||
else:
|
||||
let trimmer = unsignedTrimmer(size)
|
||||
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
|
||||
r.kind = resExpr
|
||||
|
||||
template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
@@ -617,32 +629,103 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
template applyFormat(frmtA, frmtB) =
|
||||
if i == 0: applyFormat(frmtA) else: applyFormat(frmtB)
|
||||
|
||||
template bitwiseExpr(op: string) =
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.kind in {tyUInt, tyUInt32}:
|
||||
r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc]
|
||||
else:
|
||||
r.res = "($1 $2 $3)" % [xLoc, op, yLoc]
|
||||
|
||||
case op
|
||||
of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)")
|
||||
of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)")
|
||||
of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)")
|
||||
of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
|
||||
of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
|
||||
of mAddI:
|
||||
if i == 0:
|
||||
if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
useMagic(p, "addInt64")
|
||||
applyFormat("addInt64($1, $2)")
|
||||
else:
|
||||
applyFormat("addInt($1, $2)")
|
||||
else:
|
||||
applyFormat("($1 + $2)")
|
||||
of mSubI:
|
||||
if i == 0:
|
||||
if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
useMagic(p, "subInt64")
|
||||
applyFormat("subInt64($1, $2)")
|
||||
else:
|
||||
applyFormat("subInt($1, $2)")
|
||||
else:
|
||||
applyFormat("($1 - $2)")
|
||||
of mMulI:
|
||||
if i == 0:
|
||||
if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
useMagic(p, "mulInt64")
|
||||
applyFormat("mulInt64($1, $2)")
|
||||
else:
|
||||
applyFormat("mulInt($1, $2)")
|
||||
else:
|
||||
applyFormat("($1 * $2)")
|
||||
of mDivI:
|
||||
if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
useMagic(p, "divInt64")
|
||||
applyFormat("divInt64($1, $2)", "$1 / $2")
|
||||
else:
|
||||
applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
|
||||
of mModI:
|
||||
if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
useMagic(p, "modInt64")
|
||||
applyFormat("modInt64($1, $2)", "$1 % $2")
|
||||
else:
|
||||
applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
|
||||
of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)")
|
||||
of mPred: applyFormat("subInt($1, $2)", "($1 - $2)")
|
||||
of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
|
||||
of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
|
||||
of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
|
||||
of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
|
||||
of mShrI: applyFormat("", "")
|
||||
of mShrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
else:
|
||||
if typ.kind in {tyInt..tyInt32}:
|
||||
let trimmerU = unsignedTrimmer(typ.size)
|
||||
let trimmerS = signedTrimmer(typ.size)
|
||||
r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
|
||||
else:
|
||||
applyFormat("($1 >>> $2)")
|
||||
of mShlI:
|
||||
if n[1].typ.size <= 4:
|
||||
applyFormat("($1 << $2)", "($1 << $2)")
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
|
||||
else:
|
||||
applyFormat("($1 * Math.pow(2, $2))")
|
||||
else:
|
||||
applyFormat("($1 * Math.pow(2, $2))", "($1 * Math.pow(2, $2))")
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
let trimmer = unsignedTrimmer(typ.size)
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
else:
|
||||
let trimmer = signedTrimmer(typ.size)
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
of mAshrI:
|
||||
if n[1].typ.size <= 4:
|
||||
applyFormat("($1 >> $2)", "($1 >> $2)")
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
else:
|
||||
applyFormat("Math.floor($1 / Math.pow(2, $2))")
|
||||
else:
|
||||
applyFormat("Math.floor($1 / Math.pow(2, $2))", "Math.floor($1 / Math.pow(2, $2))")
|
||||
of mBitandI: applyFormat("($1 & $2)", "($1 & $2)")
|
||||
of mBitorI: applyFormat("($1 | $2)", "($1 | $2)")
|
||||
of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)")
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
applyFormat("($1 >>> $2)")
|
||||
else:
|
||||
applyFormat("($1 >> $2)")
|
||||
of mBitandI: bitwiseExpr("&")
|
||||
of mBitorI: bitwiseExpr("|")
|
||||
of mBitxorI: bitwiseExpr("^")
|
||||
of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
|
||||
of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
|
||||
of mAddU: applyFormat("", "")
|
||||
@@ -678,7 +761,16 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mAbsI: applyFormat("absInt($1)", "Math.abs($1)")
|
||||
of mNot: applyFormat("!($1)", "!($1)")
|
||||
of mUnaryPlusI: applyFormat("+($1)", "+($1)")
|
||||
of mBitnotI: applyFormat("~($1)", "~($1)")
|
||||
of mBitnotI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.kind in {tyUInt..tyUInt64}:
|
||||
if typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asUintN(64, ~($1))")
|
||||
else:
|
||||
let trimmer = unsignedTrimmer(typ.size)
|
||||
r.res = "(~($1) $2)" % [xLoc, trimmer]
|
||||
else:
|
||||
applyFormat("~($1)")
|
||||
of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
|
||||
of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
|
||||
of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)")
|
||||
@@ -697,17 +789,14 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mMulU: binaryUintExpr(p, n, r, "*")
|
||||
of mDivU:
|
||||
binaryUintExpr(p, n, r, "/")
|
||||
if n[1].typ.skipTypes(abstractRange).size == 8:
|
||||
if optJsBigInt64 notin p.config.globalOptions and
|
||||
n[1].typ.skipTypes(abstractRange).size == 8:
|
||||
# bigint / already truncates
|
||||
r.res = "Math.trunc($1)" % [r.res]
|
||||
of mDivI:
|
||||
arithAux(p, n, r, op)
|
||||
of mModI:
|
||||
arithAux(p, n, r, op)
|
||||
of mShrI:
|
||||
var x, y: TCompRes
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc]
|
||||
of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr:
|
||||
arithAux(p, n, r, op)
|
||||
of mEqRef:
|
||||
@@ -741,6 +830,10 @@ proc genLineDir(p: PProc, n: PNode) =
|
||||
lineF(p, "$1", [lineDir(p.config, n.info, line)])
|
||||
if hasFrameInfo(p):
|
||||
lineF(p, "F.line = $1;$n", [rope(line)])
|
||||
let currentFileName = toFilename(p.config, n.info)
|
||||
if p.previousFileName != currentFileName:
|
||||
lineF(p, "F.filename = $1;$n", [makeJSString(currentFileName)])
|
||||
p.previousFileName = currentFileName
|
||||
|
||||
proc genWhileStmt(p: PProc, n: PNode) =
|
||||
var cond: TCompRes
|
||||
@@ -1105,7 +1198,7 @@ proc needsNoCopy(p: PProc; y: PNode): bool =
|
||||
return y.kind in nodeKindsNeedNoCopy or
|
||||
((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
|
||||
(skipTypes(y.typ, abstractInst).kind in
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes))
|
||||
{tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes))
|
||||
|
||||
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
var a, b: TCompRes
|
||||
@@ -1764,15 +1857,25 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v
|
||||
createRecordVarAux(p, t.n, excludedFieldIDs, output)
|
||||
t = t[0]
|
||||
|
||||
proc arrayTypeForElemType(typ: PType): string =
|
||||
proc arrayTypeForElemType(conf: ConfigRef; typ: PType): string =
|
||||
let typ = typ.skipTypes(abstractRange)
|
||||
case typ.kind
|
||||
of tyInt, tyInt32: "Int32Array"
|
||||
of tyInt16: "Int16Array"
|
||||
of tyInt8: "Int8Array"
|
||||
of tyInt64:
|
||||
if optJsBigInt64 in conf.globalOptions:
|
||||
"BigInt64Array"
|
||||
else:
|
||||
""
|
||||
of tyUInt, tyUInt32: "Uint32Array"
|
||||
of tyUInt16: "Uint16Array"
|
||||
of tyUInt8, tyChar, tyBool: "Uint8Array"
|
||||
of tyUInt64:
|
||||
if optJsBigInt64 in conf.globalOptions:
|
||||
"BigUint64Array"
|
||||
else:
|
||||
""
|
||||
of tyFloat32: "Float32Array"
|
||||
of tyFloat64, tyFloat: "Float64Array"
|
||||
of tyEnum:
|
||||
@@ -1786,11 +1889,18 @@ proc arrayTypeForElemType(typ: PType): string =
|
||||
proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
var t = skipTypes(typ, abstractInst)
|
||||
case t.kind
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
|
||||
of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
|
||||
result = putToSeq("0", indirect)
|
||||
of tyInt, tyUInt:
|
||||
if $t.sym.loc.r == "bigint":
|
||||
result = putToSeq("0n", indirect)
|
||||
else:
|
||||
result = putToSeq("0", indirect)
|
||||
of tyInt64, tyUInt64:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
result = putToSeq("0n", indirect)
|
||||
else:
|
||||
result = putToSeq("0", indirect)
|
||||
of tyFloat..tyFloat128:
|
||||
result = putToSeq("0.0", indirect)
|
||||
of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent:
|
||||
@@ -1804,7 +1914,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
of tyArray:
|
||||
let length = toInt(lengthOrd(p.config, t))
|
||||
let e = elemType(t)
|
||||
let jsTyp = arrayTypeForElemType(e)
|
||||
let jsTyp = arrayTypeForElemType(p.config, e)
|
||||
if jsTyp.len > 0:
|
||||
result = "new $1($2)" % [rope(jsTyp), rope(length)]
|
||||
elif length > 32:
|
||||
@@ -1842,7 +1952,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
result = putToSeq("null", indirect)
|
||||
of tySequence, tyString:
|
||||
result = putToSeq("[]", indirect)
|
||||
of tyCstring, tyProc:
|
||||
of tyCstring, tyProc, tyOpenArray:
|
||||
result = putToSeq("null", indirect)
|
||||
of tyStatic:
|
||||
if t.n != nil:
|
||||
@@ -1979,7 +2089,11 @@ proc genNewSeq(p: PProc, n: PNode) =
|
||||
|
||||
proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
|
||||
case skipTypes(n[1].typ, abstractVar + abstractRange).kind
|
||||
of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r)
|
||||
of tyEnum, tyInt..tyInt32, tyUInt..tyUInt32, tyChar: gen(p, n[1], r)
|
||||
of tyInt64, tyUInt64:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
unaryExpr(p, n, r, "", "Number($1)")
|
||||
else: gen(p, n[1], r)
|
||||
of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)")
|
||||
else: internalError(p.config, n.info, "genOrd")
|
||||
|
||||
@@ -2091,6 +2205,13 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) =
|
||||
genReset(p, n)
|
||||
#lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
|
||||
|
||||
proc genDup(p: PProc; n: PNode; r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
r.kind = resVal
|
||||
r.res = p.getTemp()
|
||||
gen(p, n[1], a)
|
||||
lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
|
||||
|
||||
proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var a: TCompRes
|
||||
r.res = rope("[")
|
||||
@@ -2202,14 +2323,34 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res = "($1).length - 1" % [x.rdLoc]
|
||||
r.kind = resExpr
|
||||
of mInc:
|
||||
if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
case typ.kind
|
||||
of tyUInt..tyUInt32:
|
||||
binaryUintExpr(p, n, r, "+", true)
|
||||
of tyUInt64:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 + BigInt($2))", true)
|
||||
else: binaryUintExpr(p, n, r, "+", true)
|
||||
elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
if optOverflowCheck notin p.options:
|
||||
binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 + BigInt($2))", true)
|
||||
else: binaryExpr(p, n, r, "addInt64", "$1 = addInt64($3, BigInt($2))", true)
|
||||
else:
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
|
||||
else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true)
|
||||
of ast.mDec:
|
||||
if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
case typ.kind
|
||||
of tyUInt..tyUInt32:
|
||||
binaryUintExpr(p, n, r, "-", true)
|
||||
of tyUInt64:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 - BigInt($2))", true)
|
||||
else: binaryUintExpr(p, n, r, "+", true)
|
||||
elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
if optOverflowCheck notin p.options:
|
||||
binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 - BigInt($2))", true)
|
||||
else: binaryExpr(p, n, r, "subInt64", "$1 = subInt64($3, BigInt($2))", true)
|
||||
else:
|
||||
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
|
||||
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true)
|
||||
@@ -2265,6 +2406,10 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.kind = resExpr
|
||||
of mMove:
|
||||
genMove(p, n, r)
|
||||
of mDup:
|
||||
genDup(p, n, r)
|
||||
of mEnsureMove:
|
||||
gen(p, n[1], r)
|
||||
else:
|
||||
genCall(p, n, r)
|
||||
#else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
|
||||
@@ -2303,7 +2448,7 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
## Nim sequence maps to JS array.
|
||||
var t = skipTypes(n.typ, abstractInst)
|
||||
let e = elemType(t)
|
||||
let jsTyp = arrayTypeForElemType(e)
|
||||
let jsTyp = arrayTypeForElemType(p.config, e)
|
||||
if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0:
|
||||
# generate typed array
|
||||
# for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]`
|
||||
@@ -2384,7 +2529,29 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res = "(!!($1))" % [r.res]
|
||||
r.kind = resExpr
|
||||
elif toInt:
|
||||
r.res = "(($1) | 0)" % [r.res]
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number($1)" % [r.res]
|
||||
else:
|
||||
r.res = "(($1) | 0)" % [r.res]
|
||||
elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
|
||||
r.res = "BigInt($1)" % [r.res]
|
||||
elif src.kind in {tyFloat..tyFloat64}:
|
||||
r.res = "BigInt(Math.trunc($1))" % [r.res]
|
||||
elif src.kind == tyUInt64:
|
||||
r.res = "BigInt.asIntN(64, $1)" % [r.res]
|
||||
elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
|
||||
r.res = "BigInt($1)" % [r.res]
|
||||
elif fromInt: # could be negative
|
||||
r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
|
||||
elif src.kind in {tyFloat..tyFloat64}:
|
||||
r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
|
||||
elif src.kind == tyInt64:
|
||||
r.res = "BigInt.asUintN(64, $1)" % [r.res]
|
||||
elif toUint or dest.kind in tyFloat..tyFloat64:
|
||||
if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number($1)" % [r.res]
|
||||
else:
|
||||
# TODO: What types must we handle here?
|
||||
discard
|
||||
@@ -2395,7 +2562,11 @@ proc upConv(p: PProc, n: PNode, r: var TCompRes) =
|
||||
proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
|
||||
var a, b: TCompRes
|
||||
gen(p, n[0], r)
|
||||
if optRangeCheck notin p.options or (skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64} and
|
||||
let src = skipTypes(n[0].typ, abstractVarRange)
|
||||
let dest = skipTypes(n.typ, abstractVarRange)
|
||||
if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number($1)" % [r.res]
|
||||
if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
|
||||
checkUnsignedConversions notin p.config.legacyFeatures):
|
||||
discard "XXX maybe emit masking instructions here"
|
||||
else:
|
||||
@@ -2584,24 +2755,35 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
|
||||
let fromInt = (src.kind in tyInt..tyInt32)
|
||||
let fromUint = (src.kind in tyUInt..tyUInt32)
|
||||
|
||||
if toUint and (fromInt or fromUint):
|
||||
let trimmer = unsignedTrimmer(dest.size)
|
||||
r.res = "($1 $2)" % [r.res, trimmer]
|
||||
if toUint:
|
||||
if fromInt or fromUint:
|
||||
r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
|
||||
elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res]
|
||||
elif toInt:
|
||||
if fromInt:
|
||||
return
|
||||
elif fromUint:
|
||||
if src.size == 4 and dest.size == 4:
|
||||
# XXX prevent multi evaluations
|
||||
r.res = "($1 | 0)" % [r.res]
|
||||
else:
|
||||
let trimmer = unsignedTrimmer(dest.size)
|
||||
let minuend = case dest.size
|
||||
of 1: "0xfe"
|
||||
of 2: "0xfffe"
|
||||
of 4: "0xfffffffe"
|
||||
else: ""
|
||||
r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]
|
||||
if fromInt or fromUint:
|
||||
r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
|
||||
elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
|
||||
r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res]
|
||||
elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
|
||||
r.res = "BigInt($1)" % [r.res]
|
||||
elif src.kind in {tyFloat..tyFloat64}:
|
||||
r.res = "BigInt(Math.trunc($1))" % [r.res]
|
||||
elif src.kind == tyUInt64:
|
||||
r.res = "BigInt.asIntN(64, $1)" % [r.res]
|
||||
elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
|
||||
r.res = "BigInt($1)" % [r.res]
|
||||
elif fromInt: # could be negative
|
||||
r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
|
||||
elif src.kind in {tyFloat..tyFloat64}:
|
||||
r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
|
||||
elif src.kind == tyInt64:
|
||||
r.res = "BigInt.asUintN(64, $1)" % [r.res]
|
||||
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 (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
|
||||
r.address = r.res
|
||||
r.res = "null"
|
||||
@@ -2620,10 +2802,25 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkSym:
|
||||
genSym(p, n, r)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if n.typ.kind == tyBool:
|
||||
case n.typ.skipTypes(abstractVarRange).kind
|
||||
of tyBool:
|
||||
r.res = if n.intVal == 0: rope"false" else: rope"true"
|
||||
of tyUInt64:
|
||||
r.res = rope($cast[BiggestUInt](n.intVal))
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
r.res.add('n')
|
||||
of tyInt64:
|
||||
let wrap = n.intVal < 0 # wrap negative integers with parens
|
||||
if wrap: r.res.add '('
|
||||
r.res.addInt n.intVal
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
r.res.add('n')
|
||||
if wrap: r.res.add ')'
|
||||
else:
|
||||
r.res = rope(n.intVal)
|
||||
let wrap = n.intVal < 0 # wrap negative integers with parens
|
||||
if wrap: r.res.add '('
|
||||
r.res.addInt n.intVal
|
||||
if wrap: r.res.add ')'
|
||||
r.kind = resExpr
|
||||
of nkNilLit:
|
||||
if isEmptyType(n.typ):
|
||||
@@ -2842,6 +3039,7 @@ proc processJSCodeGen*(b: PPassContext, n: PNode): PNode =
|
||||
if m.module == nil: internalError(m.config, n.info, "myProcess")
|
||||
let globals = PGlobals(m.graph.backend)
|
||||
var p = newInitProc(globals, m)
|
||||
m.initProc = p
|
||||
p.unique = globals.unique
|
||||
genModule(p, n)
|
||||
p.g.code.add(p.locals)
|
||||
@@ -2855,7 +3053,7 @@ proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
|
||||
var p = newInitProc(globals, m)
|
||||
attachProc(p, prc)
|
||||
|
||||
var disp = generateMethodDispatchers(graph)
|
||||
var disp = generateMethodDispatchers(graph, m.idgen)
|
||||
for i in 0..<disp.len:
|
||||
let prc = disp[i].sym
|
||||
if not globals.generatedSyms.containsOrIncl(prc.id):
|
||||
|
||||
@@ -140,7 +140,7 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator)
|
||||
rawAddSon(result, intType)
|
||||
|
||||
proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info)
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
|
||||
result.typ = createClosureIterStateType(g, iter, idgen)
|
||||
|
||||
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
|
||||
@@ -154,7 +154,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym
|
||||
result = iter.ast[resultPos].sym
|
||||
else:
|
||||
# XXX a bit hacky:
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {})
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
|
||||
result.typ = iter.typ[0]
|
||||
incl(result.flags, sfUsed)
|
||||
iter.ast.add newSymNode(result)
|
||||
@@ -258,8 +258,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
let iter = n.sym
|
||||
assert iter.isIterator
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
|
||||
let hp = getHiddenParam(g, iter)
|
||||
var env: PNode
|
||||
if owner.isIterator:
|
||||
@@ -267,7 +266,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
|
||||
env = indirectAccess(newSymNode(it), hp, hp.info)
|
||||
else:
|
||||
let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info)
|
||||
let e = newSym(skLet, iter.name, idgen, owner, n.info)
|
||||
e.typ = hp.typ
|
||||
e.flags = hp.flags
|
||||
env = newSymNode(e)
|
||||
@@ -293,23 +292,26 @@ proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner:
|
||||
|
||||
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
let s = n.sym
|
||||
let isEnv = s.name.id == getIdent(g.cache, ":env").id
|
||||
if illegalCapture(s):
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
||||
" Consider using a <ref $2> which can be captured.") %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
|
||||
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)
|
||||
owner.typ.callConv = ccClosure
|
||||
if not isEnv:
|
||||
owner.typ.callConv = ccClosure
|
||||
|
||||
type
|
||||
DetectionPass = object
|
||||
processed, capturedVars: IntSet
|
||||
ownerToType: Table[int, PType]
|
||||
somethingToDo: bool
|
||||
inTypeOf: bool
|
||||
graph: ModuleGraph
|
||||
idgen: IdGenerator
|
||||
|
||||
@@ -380,7 +382,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
|
||||
localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
|
||||
else:
|
||||
let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info)
|
||||
let result = newSym(skField, upIdent, c.idgen, obj.owner, obj.owner.info)
|
||||
result.typ = fieldType
|
||||
when false:
|
||||
if c.graph.config.selectedGC == gcDestructors:
|
||||
@@ -413,12 +415,15 @@ Consider:
|
||||
|
||||
"""
|
||||
|
||||
proc isTypeOf(n: PNode): bool =
|
||||
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
|
||||
|
||||
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
var cp = getEnvParam(fn)
|
||||
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
|
||||
let t = c.getEnvTypeForOwner(owner, info)
|
||||
if cp == nil:
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info)
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info)
|
||||
incl(cp.flags, sfFromGeneric)
|
||||
cp.typ = t
|
||||
addHiddenParam(fn, cp)
|
||||
@@ -444,12 +449,15 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
let body = transformBody(c.graph, c.idgen, s, useCache)
|
||||
detectCapturedVars(body, s, c)
|
||||
let ow = s.skipGenericOwner
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
|
||||
let interested = interestingVar(s)
|
||||
if ow == owner:
|
||||
if owner.isIterator:
|
||||
c.somethingToDo = true
|
||||
addClosureParam(c, owner, n.info)
|
||||
if interestingIterVar(s):
|
||||
if not c.capturedVars.containsOrIncl(s.id):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
|
||||
@@ -458,7 +466,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
else:
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
# direct or indirect dependency:
|
||||
elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s):
|
||||
elif innerClosure or interested:
|
||||
discard """
|
||||
proc outer() =
|
||||
var x: int
|
||||
@@ -475,10 +483,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
addClosureParam(c, owner, n.info)
|
||||
#echo "capturing ", n.info
|
||||
# variable 's' is actually captured:
|
||||
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
if interestingVar(s):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
# create required upFields:
|
||||
var w = owner.skipGenericOwner
|
||||
if isInnerProc(w) or owner.isIterator:
|
||||
@@ -510,9 +520,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
detectCapturedVars(n[namePos], owner, c)
|
||||
of nkReturnStmt:
|
||||
detectCapturedVars(n[0], owner, c)
|
||||
of nkIdentDefs:
|
||||
detectCapturedVars(n[^1], owner, c)
|
||||
else:
|
||||
if n.isCallExpr and n[0].isTypeOf:
|
||||
c.inTypeOf = true
|
||||
for i in 0..<n.len:
|
||||
detectCapturedVars(n[i], owner, c)
|
||||
c.inTypeOf = false
|
||||
|
||||
type
|
||||
LiftingPass = object
|
||||
@@ -545,7 +560,7 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||
result = n
|
||||
|
||||
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
|
||||
var v = newSym(skVar, getIdent(cache, envName), nextSymId(idgen), owner, info)
|
||||
var v = newSym(skVar, getIdent(cache, envName), idgen, owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = typ
|
||||
result = newSymNode(v)
|
||||
@@ -569,7 +584,7 @@ proc setupEnvVar(owner: PSym; d: var DetectionPass;
|
||||
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
|
||||
c.envVars[owner.id] = result
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextSymId d.idgen, owner, info)
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), d.idgen, owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = envVarType
|
||||
c.unownedEnvVars[owner.id] = newSymNode(v)
|
||||
@@ -653,7 +668,7 @@ proc closureCreationForIter(iter: PNode;
|
||||
d: var DetectionPass; c: var LiftingPass): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), nextSymId(d.idgen), owner, iter.info)
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
@@ -792,6 +807,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
of nkTypeOfExpr:
|
||||
result = n
|
||||
else:
|
||||
if n.isCallExpr and n[0].isTypeOf:
|
||||
return
|
||||
if owner.isIterator:
|
||||
if nfLL in n.flags:
|
||||
# special case 'when nimVm' due to bug #3636:
|
||||
@@ -943,7 +960,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
let iter = op.sym
|
||||
|
||||
let hp = getHiddenParam(g, iter)
|
||||
env = newSym(skLet, iter.name, nextSymId(idgen), owner, body.info)
|
||||
env = newSym(skLet, iter.name, idgen, owner, body.info)
|
||||
env.typ = hp.typ
|
||||
env.flags = hp.flags
|
||||
|
||||
@@ -971,12 +988,15 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
# gather vars in a tuple:
|
||||
var v2 = newNodeI(nkLetSection, body.info)
|
||||
var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
|
||||
for i in 0..<body.len-2:
|
||||
if body[i].kind == nkSym:
|
||||
body[i].sym.transitionToLet()
|
||||
vpart.add body[i]
|
||||
if body.len == 3 and body[0].kind == nkVarTuple:
|
||||
vpart = body[0] # fixes for (i,j) in walk() # bug #15924
|
||||
else:
|
||||
for i in 0..<body.len-2:
|
||||
if body[i].kind == nkSym:
|
||||
body[i].sym.transitionToLet()
|
||||
vpart.add body[i]
|
||||
|
||||
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
|
||||
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
|
||||
if not env.isNil:
|
||||
call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
|
||||
vpart.add call
|
||||
|
||||
@@ -510,7 +510,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
rememberSplit(splitComma)
|
||||
wrSpace em
|
||||
of openPars:
|
||||
if tok.strongSpaceA and not em.endsInWhite and
|
||||
if tsLeading in tok.spacing and not em.endsInWhite and
|
||||
(not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
|
||||
wrSpace em
|
||||
wr(em, $tok.tokType, ltSomeParLe)
|
||||
@@ -528,7 +528,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
wr(em, $tok.tokType, ltOther)
|
||||
if not em.inquote: wrSpace(em)
|
||||
of tkOpr, tkDotDot:
|
||||
if em.inquote or (((not tok.strongSpaceA) and tok.strongSpaceB == tsNone) and
|
||||
if em.inquote or (tok.spacing == {} and
|
||||
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
|
||||
# bug #9504: remember to not spacify a keyword:
|
||||
lastTokWasTerse = true
|
||||
@@ -538,7 +538,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
if not em.endsInWhite: wrSpace(em)
|
||||
wr(em, tok.ident.s, ltOpr)
|
||||
template isUnary(tok): bool =
|
||||
tok.strongSpaceB == tsNone and tok.strongSpaceA
|
||||
tok.spacing == {tsLeading}
|
||||
|
||||
if not isUnary(tok):
|
||||
rememberSplit(splitBinary)
|
||||
|
||||
@@ -23,7 +23,6 @@ when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
|
||||
const
|
||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||
numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
|
||||
SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
|
||||
SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
|
||||
@@ -94,19 +93,18 @@ type
|
||||
base2, base8, base16
|
||||
|
||||
TokenSpacing* = enum
|
||||
tsNone, tsTrailing, tsEof
|
||||
tsLeading, tsTrailing, tsEof
|
||||
|
||||
Token* = object # a Nim token
|
||||
tokType*: TokType # the type of the token
|
||||
base*: NumericalBase # the numerical base; only valid for int
|
||||
# or float literals
|
||||
spacing*: set[TokenSpacing] # spaces around token
|
||||
indent*: int # the indentation; != -1 if the token has been
|
||||
# preceded with indentation
|
||||
ident*: PIdent # the parsed identifier
|
||||
iNumber*: BiggestInt # the parsed integer literal
|
||||
fNumber*: BiggestFloat # the parsed floating point literal
|
||||
base*: NumericalBase # the numerical base; only valid for int
|
||||
# or float literals
|
||||
strongSpaceA*: bool # leading spaces of an operator
|
||||
strongSpaceB*: TokenSpacing # trailing spaces of an operator
|
||||
literal*: string # the parsed (string) literal; and
|
||||
# documentation comments are here too
|
||||
line*, col*: int
|
||||
@@ -178,7 +176,7 @@ proc initToken*(L: var Token) =
|
||||
L.tokType = tkInvalid
|
||||
L.iNumber = 0
|
||||
L.indent = 0
|
||||
L.strongSpaceA = false
|
||||
L.spacing = {}
|
||||
L.literal = ""
|
||||
L.fNumber = 0.0
|
||||
L.base = base10
|
||||
@@ -191,7 +189,7 @@ proc fillToken(L: var Token) =
|
||||
L.tokType = tkInvalid
|
||||
L.iNumber = 0
|
||||
L.indent = 0
|
||||
L.strongSpaceA = false
|
||||
L.spacing = {}
|
||||
setLen(L.literal, 0)
|
||||
L.fNumber = 0.0
|
||||
L.base = base10
|
||||
@@ -737,10 +735,6 @@ proc handleCRLF(L: var Lexer, pos: int): int =
|
||||
template registerLine =
|
||||
let col = L.getColNumber(pos)
|
||||
|
||||
when not defined(nimpretty):
|
||||
if col > MaxLineLength:
|
||||
lexMessagePos(L, hintLineTooLong, pos)
|
||||
|
||||
case L.buf[pos]
|
||||
of CR:
|
||||
registerLine()
|
||||
@@ -960,13 +954,15 @@ proc getOperator(L: var Lexer, tok: var Token) =
|
||||
tokenEnd(tok, pos-1)
|
||||
# advance pos but don't store it in L.bufpos so the next token (which might
|
||||
# be an operator too) gets the preceding spaces:
|
||||
tok.strongSpaceB = tsNone
|
||||
tok.spacing = tok.spacing - {tsTrailing, tsEof}
|
||||
var trailing = false
|
||||
while L.buf[pos] == ' ':
|
||||
inc pos
|
||||
if tok.strongSpaceB != tsTrailing:
|
||||
tok.strongSpaceB = tsTrailing
|
||||
trailing = true
|
||||
if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||
tok.strongSpaceB = tsEof
|
||||
tok.spacing.incl(tsEof)
|
||||
elif trailing:
|
||||
tok.spacing.incl(tsTrailing)
|
||||
|
||||
proc getPrecedence*(tok: Token): int =
|
||||
## Calculates the precedence of the given token.
|
||||
@@ -1077,7 +1073,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
|
||||
when defined(nimpretty): tok.literal.add "\L"
|
||||
if isDoc:
|
||||
when not defined(nimpretty): tok.literal.add "\n"
|
||||
inc tok.iNumber
|
||||
var c = toStrip
|
||||
while L.buf[pos] == ' ' and c > 0:
|
||||
inc pos
|
||||
@@ -1096,8 +1091,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
|
||||
proc scanComment(L: var Lexer, tok: var Token) =
|
||||
var pos = L.bufpos
|
||||
tok.tokType = tkComment
|
||||
# iNumber contains the number of '\n' in the token
|
||||
tok.iNumber = 0
|
||||
assert L.buf[pos+1] == '#'
|
||||
when defined(nimpretty):
|
||||
tok.commentOffsetA = L.offsetBase + pos
|
||||
@@ -1140,7 +1133,6 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||
while L.buf[pos] == ' ' and c > 0:
|
||||
inc pos
|
||||
dec c
|
||||
inc tok.iNumber
|
||||
else:
|
||||
if L.buf[pos] > ' ':
|
||||
L.indentAhead = indent
|
||||
@@ -1153,7 +1145,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||
proc skip(L: var Lexer, tok: var Token) =
|
||||
var pos = L.bufpos
|
||||
tokenBegin(tok, pos)
|
||||
tok.strongSpaceA = false
|
||||
tok.spacing.excl(tsLeading)
|
||||
when defined(nimpretty):
|
||||
var hasComment = false
|
||||
var commentIndent = L.currLineIndent
|
||||
@@ -1164,8 +1156,7 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||
case L.buf[pos]
|
||||
of ' ':
|
||||
inc(pos)
|
||||
if not tok.strongSpaceA:
|
||||
tok.strongSpaceA = true
|
||||
tok.spacing.incl(tsLeading)
|
||||
of '\t':
|
||||
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
|
||||
inc(pos)
|
||||
@@ -1187,7 +1178,7 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||
pos = L.bufpos
|
||||
else:
|
||||
break
|
||||
tok.strongSpaceA = false
|
||||
tok.spacing.excl(tsLeading)
|
||||
when defined(nimpretty):
|
||||
if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
|
||||
if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
## This module implements lifting for type-bound operations
|
||||
## (``=sink``, ``=copy``, ``=destroy``, ``=deepCopy``).
|
||||
## (`=sink`, `=copy`, `=destroy`, `=deepCopy`, `=wasMoved`, `=dup`).
|
||||
|
||||
import modulegraphs, lineinfos, idents, ast, renderer, semdata,
|
||||
sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
|
||||
@@ -34,11 +34,12 @@ type
|
||||
|
||||
template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
|
||||
template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
|
||||
template dup*(t: PType): PSym = getAttachedOp(c.g, t, attachedDup)
|
||||
template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
|
||||
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
idgen: IdGenerator)
|
||||
@@ -49,9 +50,9 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result[1] = i
|
||||
result.typ = elemType
|
||||
|
||||
proc destructorOverriden(g: ModuleGraph; t: PType): bool =
|
||||
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
op != nil and sfOverriden in op.flags
|
||||
op != nil and sfOverridden in op.flags
|
||||
|
||||
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
for i in 0..<t.len:
|
||||
@@ -82,14 +83,14 @@ proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode =
|
||||
result = genBuiltin(c.g, c.idgen, magic, name, i)
|
||||
|
||||
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink, attachedDup}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
elif c.kind == attachedDestructor and c.addMemReset:
|
||||
let call = genBuiltin(c, mDefault, "default", x)
|
||||
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:
|
||||
@@ -139,11 +140,14 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
|
||||
proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
var destroy = newNodeIT(nkCall, x.info, op.typ[0])
|
||||
destroy.add(newSymNode(op))
|
||||
destroy.add genAddr(c, x)
|
||||
if op.typ[1].kind != tyVar:
|
||||
destroy.add x
|
||||
else:
|
||||
destroy.add genAddr(c, x)
|
||||
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
|
||||
|
||||
@@ -152,17 +156,18 @@ proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
result.add(newSymNode(op))
|
||||
result.add genAddr(c, x)
|
||||
|
||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
|
||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if c.filterDiscriminator != nil: return
|
||||
let f = n.sym
|
||||
let b = if c.kind == attachedTrace: y else: y.dotField(f)
|
||||
if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
|
||||
c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}) or
|
||||
if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
|
||||
enforceDefaultOp:
|
||||
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||
else:
|
||||
if enforceWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
|
||||
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
@@ -201,7 +206,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
|
||||
branch[^1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
|
||||
enforceDefaultOp = localEnforceDefaultOp)
|
||||
enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
|
||||
if branch[^1].len == 0: inc emptyBranches
|
||||
caseStmt.add(branch)
|
||||
if emptyBranches != n.len-1:
|
||||
@@ -212,13 +217,16 @@ 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)
|
||||
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.len > 0 and t[0] != nil:
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
|
||||
let obj = newNodeIT(nkHiddenSubConv, c.info, t[0])
|
||||
obj.add newNodeI(nkEmpty, c.info)
|
||||
obj.add x
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, obj, y)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
@@ -243,7 +251,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
# for every field (dependent on dest.kind):
|
||||
# `=` dest.field, src.field
|
||||
# =destroy(blob)
|
||||
var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
|
||||
var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
dummy.typ = y.typ
|
||||
if ccgIntroducedPtr(c.g.config, dummy, y.typ):
|
||||
# Because of potential aliasing when the src param is passed by ref, we need to check for equality here,
|
||||
@@ -252,7 +260,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, 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)
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
@@ -262,7 +270,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
#body.add newAsgnStmt(blob, x)
|
||||
|
||||
var wasMovedCall = newNodeI(nkCall, c.info)
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, 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
|
||||
|
||||
@@ -275,6 +283,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
c.kind = attachedDestructor
|
||||
fillBodyObjTImpl(c, t, body, blob, y)
|
||||
c.kind = prevKind
|
||||
|
||||
else:
|
||||
fillBodyObjTImpl(c, t, body, x, y)
|
||||
|
||||
@@ -283,7 +292,7 @@ proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
|
||||
result.typ = getSysType(g, info, tyBool)
|
||||
|
||||
proc getCycleParam(c: TLiftCtx): PNode =
|
||||
assert c.kind == attachedAsgn
|
||||
assert c.kind in {attachedAsgn, attachedDup}
|
||||
if c.fn.typ.len == 4:
|
||||
result = c.fn.typ.n.lastSon
|
||||
assert result.kind == nkSym
|
||||
@@ -322,6 +331,9 @@ proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(c, op, y))
|
||||
|
||||
proc newDupCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(c, op, y))
|
||||
|
||||
proc usesBuiltinArc(t: PType): bool =
|
||||
proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t)
|
||||
result = types.searchTypeFor(t, wrap)
|
||||
@@ -346,9 +358,9 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
field: var PSym): bool =
|
||||
if optSeqDestructors in c.g.config.globalOptions:
|
||||
var op = field
|
||||
let destructorOverriden = destructorOverriden(c.g, t)
|
||||
let destructorOverridden = destructorOverridden(c.g, t)
|
||||
if op != nil and op != c.fn and
|
||||
(sfOverriden in op.flags or destructorOverriden):
|
||||
(sfOverridden in op.flags or destructorOverridden):
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
#else:
|
||||
@@ -356,7 +368,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
onUse(c.info, op)
|
||||
body.add newHookCall(c, op, x, y)
|
||||
result = true
|
||||
elif op == nil and destructorOverriden:
|
||||
elif op == nil and destructorOverridden:
|
||||
op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
|
||||
body.add newHookCall(c, op, x, y)
|
||||
result = true
|
||||
@@ -394,7 +406,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
|
||||
let t = orig.skipTypes(abstractInst - {tyDistinct})
|
||||
var op = t.destructor
|
||||
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
@@ -417,7 +429,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
case c.kind
|
||||
of attachedDestructor:
|
||||
var op = t.destructor
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
@@ -431,7 +443,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
#result = addDestructorCall(c, t, body, x)
|
||||
of attachedAsgn, attachedSink, attachedTrace:
|
||||
var op = getAttachedOp(c.g, t, c.kind)
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic =trace:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
@@ -451,7 +463,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
|
||||
of attachedWasMoved:
|
||||
var op = getAttachedOp(c.g, t, attachedWasMoved)
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
@@ -463,8 +475,22 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
body.add genWasMovedCall(c, op, x)
|
||||
result = true
|
||||
|
||||
of attachedDup:
|
||||
var op = getAttachedOp(c.g, t, attachedDup)
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
setAttachedOp(c.g, c.idgen.module, t, attachedDup, op)
|
||||
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add newDupCall(c, op, x, y)
|
||||
result = true
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
|
||||
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)
|
||||
|
||||
@@ -474,7 +500,7 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
body.add v
|
||||
|
||||
proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
temp.typ = value.typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
@@ -521,14 +547,28 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
else:
|
||||
body.sons.setLen counterIdx
|
||||
|
||||
proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var cond = callCodegenProc(c.g, "sameSeqPayload", c.info,
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
)
|
||||
cond.typ = getSysType(c.g, c.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
|
||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedDup:
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
# we generate:
|
||||
# if x.p == y.p:
|
||||
# return
|
||||
# setLen(dest, y.len)
|
||||
# var i = 0
|
||||
# while i < y.len: dest[i] = y[i]; inc(i)
|
||||
# This is usually more efficient than a destroy/create pair.
|
||||
checkSelfAssignment(c, t, body, x, y)
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedSink:
|
||||
@@ -542,17 +582,17 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
forallElements(c, t, body, x, y)
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
if canFormAcycle(t.elemType):
|
||||
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)
|
||||
# recursions are tricky, so we might need to forward the generated
|
||||
# operation here:
|
||||
var t = t
|
||||
if t.assignment == nil or t.destructor == nil:
|
||||
if t.assignment == nil or t.destructor == nil or t.dup == nil:
|
||||
let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct})
|
||||
let canon = c.g.canonTypes.getOrDefault(h)
|
||||
if canon != nil: t = canon
|
||||
@@ -578,16 +618,22 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
doAssert t.destructor != nil
|
||||
body.add destructorCall(c, t.destructor, x)
|
||||
of attachedTrace:
|
||||
if t.kind != tyString and canFormAcycle(t.elemType):
|
||||
if t.kind != tyString and canFormAcycle(c.g, t.elemType):
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
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)
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newDupCall(c, op, x, y)
|
||||
|
||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
of attachedAsgn, attachedDeepCopy, attachedDup:
|
||||
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
@@ -599,11 +645,11 @@ 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*(t: PType): bool =
|
||||
proc cyclicType*(g: ModuleGraph, t: PType): bool =
|
||||
case t.kind
|
||||
of tyRef: result = types.canFormAcycle(t.lastSon)
|
||||
of tyRef: result = types.canFormAcycle(g, t.lastSon)
|
||||
of tyProc: result = t.callConv == ccClosure
|
||||
else: result = false
|
||||
|
||||
@@ -631,7 +677,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let elemType = t.lastSon
|
||||
|
||||
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
|
||||
|
||||
let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and
|
||||
(not isPureObject(elemType)) and
|
||||
@@ -639,7 +685,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# dynamic Acyclic refs need to use dyn decRef
|
||||
|
||||
let tmp =
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
|
||||
declareTempOf(c, body, x)
|
||||
else:
|
||||
x
|
||||
@@ -698,8 +744,16 @@ 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)
|
||||
body.add genIf(c, y, callCodegenProc(c.g,
|
||||
"nimIncRefCyclic", c.info, y, getCycleParam(c)))
|
||||
else:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, y, callCodegenProc(c.g,
|
||||
"nimIncRef", c.info, y))
|
||||
|
||||
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Closures are really like refs except they always use a virtual destructor
|
||||
@@ -709,7 +763,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||
let tmp =
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
|
||||
declareTempOf(c, body, xenv)
|
||||
else:
|
||||
xenv
|
||||
@@ -743,12 +797,21 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
body.add genIf(c, cond, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
else:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
|
||||
of attachedDestructor:
|
||||
body.add genIf(c, cond, actions)
|
||||
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
|
||||
@@ -761,6 +824,9 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
|
||||
body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
|
||||
of attachedDestructor:
|
||||
# it's better to prepend the destruction of weak refs in order to
|
||||
# prevent wrong "dangling refs exist" problems:
|
||||
@@ -773,7 +839,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)
|
||||
@@ -795,11 +861,13 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedSink, attachedAsgn:
|
||||
body.add genIf(c, x, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDestructor:
|
||||
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:
|
||||
@@ -811,7 +879,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
call[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
@@ -826,6 +894,11 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
of attachedDestructor:
|
||||
let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
if body.len == 0:
|
||||
@@ -834,7 +907,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)
|
||||
@@ -846,11 +919,13 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedSink, attachedAsgn:
|
||||
body.add genIf(c, xx, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDestructor:
|
||||
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
|
||||
@@ -859,7 +934,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyPtr, tyUncheckedArray, tyVar, tyLent:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyRef:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
atomicRefOp(c, t, body, x, y)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options):
|
||||
@@ -868,7 +943,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyProc:
|
||||
if t.callConv == ccClosure:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
atomicClosureOp(c, t, body, x, y)
|
||||
else:
|
||||
closureOp(c, t, body, x, y)
|
||||
@@ -914,10 +989,25 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyObject:
|
||||
if not considerUserDefinedOp(c, t, body, x, y):
|
||||
if c.kind in {attachedAsgn, attachedSink} and t.sym != nil and sfImportc in t.sym.flags:
|
||||
body.add newAsgnStmt(x, y)
|
||||
if t.sym != nil and sfImportc in t.sym.flags:
|
||||
case c.kind
|
||||
of {attachedAsgn, attachedSink, attachedDup}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
if c.kind == attachedDup:
|
||||
var op2 = getAttachedOp(c.g, t, attachedAsgn)
|
||||
if op2 != nil and sfOverridden in op2.flags:
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op2)
|
||||
body.add newHookCall(c, t.assignment, x, y)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
of tyDistinct:
|
||||
if not considerUserDefinedOp(c, t, body, x, y):
|
||||
fillBody(c, t[0], body, x, y)
|
||||
@@ -946,19 +1036,62 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
if getAttachedOp(g, baseType, kind) == nil:
|
||||
discard produceSym(g, c, baseType, kind, info, idgen)
|
||||
# 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)
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
result = newSym(skProc, procname, idgen, owner, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, "src"),
|
||||
idgen, result, info)
|
||||
res.typ = typ
|
||||
src.typ = typ
|
||||
|
||||
result.typ = newType(tyProc, nextTypeId idgen, owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
|
||||
result.typ.addParam src
|
||||
|
||||
if g.config.selectedGC == gcOrc and
|
||||
cyclicType(g, typ.skipTypes(abstractInst)):
|
||||
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
|
||||
idgen, result, info)
|
||||
cycleParam.typ = getSysType(g, info, tyBool)
|
||||
result.typ.addParam cycleParam
|
||||
|
||||
var n = newNodeI(nkProcDef, info, bodyPos+2)
|
||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
||||
n[namePos] = newSymNode(result)
|
||||
n[paramsPos] = result.typ.n
|
||||
n[bodyPos] = newNodeI(nkStmtList, info)
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfGeneratedOp
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
result = newSym(skProc, procname, nextSymId(idgen), owner, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
|
||||
result = newSym(skProc, procname, idgen, owner, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
|
||||
nextSymId(idgen), result, info)
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
idgen, result, info)
|
||||
|
||||
if kind == attachedDestructor and typ.kind in {tyRef, tyString, tySequence} and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and not isDistinct:
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
|
||||
if kind == attachedTrace:
|
||||
src.typ = getSysType(g, info, tyPointer)
|
||||
else:
|
||||
@@ -970,9 +1103,9 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
result.typ.addParam src
|
||||
|
||||
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
|
||||
cyclicType(typ.skipTypes(abstractInst)):
|
||||
cyclicType(g, typ.skipTypes(abstractInst)):
|
||||
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
|
||||
nextSymId(idgen), result, info)
|
||||
idgen, result, info)
|
||||
cycleParam.typ = getSysType(g, info, tyBool)
|
||||
result.typ.addParam cycleParam
|
||||
|
||||
@@ -984,6 +1117,9 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfGeneratedOp
|
||||
if kind == attachedWasMoved:
|
||||
incl result.flags, sfNoSideEffect
|
||||
incl result.typ.flags, tfNoSideEffect
|
||||
|
||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
||||
@@ -993,33 +1129,36 @@ 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): PSym =
|
||||
info: TLineInfo; idgen: IdGenerator; isDistinct = false): 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)
|
||||
result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
|
||||
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
|
||||
let dest = result.typ.n[1].sym
|
||||
let d = newDeref(newSymNode(dest))
|
||||
let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym
|
||||
let d = if result.typ[1].kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
|
||||
let src = case kind
|
||||
of {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
of attachedDup: newSymNode(result.typ.n[1].sym)
|
||||
else: newSymNode(result.typ.n[2].sym)
|
||||
|
||||
# register this operation already:
|
||||
setAttachedOpPartial(g, idgen.module, typ, kind, result)
|
||||
|
||||
if kind == attachedSink and destructorOverriden(g, typ):
|
||||
if kind == attachedSink and destructorOverridden(g, typ):
|
||||
## compiler can use a combination of `=destroy` and memCopy for sink op
|
||||
dest.flags.incl sfCursor
|
||||
result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
|
||||
let op = getAttachedOp(g, typ, attachedDestructor)
|
||||
result.ast[bodyPos].add newOpCall(a, op, if op.typ[1].kind == tyVar: d[0] else: d)
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
else:
|
||||
var tk: TTypeKind
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
|
||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||
else:
|
||||
tk = tyNone # no special casing for strings and seqs
|
||||
@@ -1030,7 +1169,7 @@ 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} and not lacksMTypeField(typ):
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
@@ -1049,7 +1188,7 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
a.addMemReset = true
|
||||
let discrimantDest = result.typ.n[1].sym
|
||||
|
||||
let dst = newSym(skVar, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
|
||||
let dst = newSym(skVar, getIdent(g.cache, "dest"), idgen, result, info)
|
||||
dst.typ = makePtrType(typ.owner, typ, idgen)
|
||||
let dstSym = newSymNode(dst)
|
||||
let d = newDeref(dstSym)
|
||||
@@ -1075,7 +1214,7 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
|
||||
if op != nil:
|
||||
if op.ast.isGenericRoutine:
|
||||
internalError(g.config, info, "resolved destructor is generic")
|
||||
if op.magic == mDestroy:
|
||||
if op.magic == mDestroy and t.kind != tyString:
|
||||
internalError(g.config, info, "patching mDestroy with mDestroy?")
|
||||
n[0] = newSymNode(op)
|
||||
for x in n: patchBody(g, c, x, info, idgen)
|
||||
@@ -1093,7 +1232,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 isTrival*(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;
|
||||
|
||||
@@ -44,6 +44,7 @@ type
|
||||
errRstSandboxedDirective,
|
||||
errProveInit, # deadcode
|
||||
errGenerated,
|
||||
errFailedMove,
|
||||
errUser,
|
||||
# warnings
|
||||
warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
|
||||
@@ -83,16 +84,18 @@ type
|
||||
warnCstringConv = "CStringConv",
|
||||
warnPtrToCstringConv = "PtrToCstringConv",
|
||||
warnEffect = "Effect",
|
||||
warnCastSizes = "CastSizes"
|
||||
warnCastSizes = "CastSizes", # deadcode
|
||||
warnAboveMaxSizeSet = "AboveMaxSizeSet",
|
||||
warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
|
||||
warnUnnamedBreak = "UnnamedBreak",
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnGenericsIgnoredInjection = "GenericsIgnoredInjection",
|
||||
warnUser = "User",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
hintCC = "CC",
|
||||
hintLineTooLong = "LineTooLong",
|
||||
hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport",
|
||||
hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
|
||||
hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
|
||||
@@ -106,6 +109,7 @@ type
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -127,6 +131,7 @@ const
|
||||
errRstSandboxedDirective: "disabled directive: '$1'",
|
||||
errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
|
||||
errGenerated: "$1",
|
||||
errFailedMove: "$1",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open '$1'",
|
||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
|
||||
@@ -182,19 +187,21 @@ const
|
||||
warnAnyEnumConv: "$1",
|
||||
warnHoleEnumConv: "$1",
|
||||
warnCstringConv: "$1",
|
||||
warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; this will become a compile time error in the future",
|
||||
warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; Use a `cast` operation like `cast[cstring](x)`; this will become a compile time error in the future",
|
||||
warnEffect: "$1",
|
||||
warnCastSizes: "$1",
|
||||
warnCastSizes: "$1", # deadcode
|
||||
warnAboveMaxSizeSet: "$1",
|
||||
warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
|
||||
warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
|
||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||
warnBareExcept: "$1",
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnGenericsIgnoredInjection: "$1",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
||||
hintCC: "CC: $1",
|
||||
hintLineTooLong: "line too long",
|
||||
hintXDeclaredButNotUsed: "'$1' is declared but not used",
|
||||
hintDuplicateModuleImport: "$1",
|
||||
hintXCannotRaiseY: "$1",
|
||||
@@ -227,6 +234,7 @@ const
|
||||
hintExtendedContext: "$1",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
]
|
||||
|
||||
const
|
||||
@@ -244,7 +252,7 @@ type
|
||||
TNoteKinds* = set[TNoteKind]
|
||||
|
||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv}
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept}
|
||||
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
|
||||
result[1] = result[2] - {warnProveField, warnProveIndex,
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
@@ -275,7 +283,7 @@ type
|
||||
# and parsed; usually "" but is used
|
||||
# for 'nimsuggest'
|
||||
hash*: string # the checksum of the file
|
||||
dirty*: bool # for 'nimfix' / 'nimpretty' like tooling
|
||||
dirty*: bool # for 'nimpretty' like tooling
|
||||
when defined(nimpretty):
|
||||
fullContent*: string
|
||||
FileIndex* = distinct int32
|
||||
|
||||
@@ -95,7 +95,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
sym.kind != skResult and # ignore `result`
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
@@ -136,7 +136,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
|
||||
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
||||
@@ -147,6 +147,10 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
|
||||
if pragmaName != wanted:
|
||||
lintReport(conf, info, wanted, pragmaName)
|
||||
|
||||
template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
||||
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
|
||||
checkPragmaUseImpl(conf, info, w, pragmaName)
|
||||
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
|
||||
## Check builtin pragma uses match their definition's style.
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
#
|
||||
|
||||
# This module implements lookup helpers.
|
||||
import std/[algorithm, strutils]
|
||||
import std/[algorithm, strutils, tables]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import
|
||||
intsets, ast, astalgo, idents, semdata, types, msgs, options,
|
||||
renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs, sets
|
||||
renderer, lineinfos, modulegraphs, astmsgs, sets, wordrecg
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
|
||||
|
||||
@@ -94,17 +94,6 @@ iterator localScopesFrom*(c: PContext; scope: PScope): PScope =
|
||||
if s == c.topLevelScope: break
|
||||
yield s
|
||||
|
||||
proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
|
||||
if s == nil or s.kind != skAlias:
|
||||
result = s
|
||||
else:
|
||||
result = s.owner
|
||||
if conf.cmd == cmdNimfix:
|
||||
prettybase.replaceDeprecated(conf, n.info, s, result)
|
||||
else:
|
||||
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
|
||||
s.name.s & " is deprecated")
|
||||
|
||||
proc isShadowScope*(s: PScope): bool {.inline.} =
|
||||
s.parent != nil and s.parent.depthLevel == s.depthLevel
|
||||
|
||||
@@ -265,6 +254,41 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
|
||||
result = false
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
var scopeHasCandidate = false
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
if scopeHasCandidate:
|
||||
# 2 candidates in same scope, ambiguous
|
||||
return true
|
||||
else:
|
||||
scopeHasCandidate = true
|
||||
sym = candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
if scopeHasCandidate:
|
||||
# scope had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
var importsHaveCandidate = false
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
if importsHaveCandidate:
|
||||
# 2 candidates among imports, ambiguous
|
||||
return true
|
||||
else:
|
||||
importsHaveCandidate = true
|
||||
sym = s
|
||||
if importsHaveCandidate:
|
||||
# imports had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
var m = n
|
||||
@@ -274,7 +298,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
considerQuotedIdent(c, m)
|
||||
else:
|
||||
getIdent(c.cache, "err:" & renderTree(m))
|
||||
result = newSym(skError, ident, nextSymId(c.idgen), getCurrOwner(c), n.info, {})
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), n.info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
@@ -340,17 +364,18 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
||||
# xxx pending bootstrap >= 1.4, replace all those overloads with a single one:
|
||||
# proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} =
|
||||
proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
||||
if sym.name.s == "_": return
|
||||
if sym.name.id == ord(wUnderscore): return
|
||||
let conflict = scope.addUniqueSym(sym)
|
||||
if conflict != nil:
|
||||
if sym.kind == skModule and conflict.kind == skModule and
|
||||
sym.position == conflict.position:
|
||||
if sym.kind == skModule and conflict.kind == skModule:
|
||||
# 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.
|
||||
localError(c.config, info, hintDuplicateModuleImport,
|
||||
"duplicate import of '$1'; previous import here: $2" %
|
||||
[sym.name.s, c.config $ conflict.info])
|
||||
if c.importModuleMap[sym.id] == c.importModuleMap[conflict.id]:
|
||||
#only hints if the conflict is the actual module not just a shared name
|
||||
localError(c.config, info, hintDuplicateModuleImport,
|
||||
"duplicate import of '$1'; previous import here: $2" %
|
||||
[sym.name.s, c.config $ conflict.info])
|
||||
else:
|
||||
wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated)
|
||||
|
||||
@@ -396,11 +421,12 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
|
||||
if fn.kind notin OverloadableSyms:
|
||||
internalError(c.config, fn.info, "addOverloadableSymAt")
|
||||
return
|
||||
let check = strTableGet(scope.symbols, fn.name)
|
||||
if check != nil and check.kind notin OverloadableSyms:
|
||||
wrongRedefinition(c, fn.info, fn.name.s, check.info)
|
||||
else:
|
||||
scope.addSym(fn)
|
||||
if fn.name.id != ord(wUnderscore):
|
||||
let check = strTableGet(scope.symbols, fn.name)
|
||||
if check != nil and check.kind notin OverloadableSyms:
|
||||
wrongRedefinition(c, fn.info, fn.name.s, check.info)
|
||||
else:
|
||||
scope.addSym(fn)
|
||||
|
||||
proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
|
||||
## adds an overloadable symbol on the scope and the interface if appropriate
|
||||
@@ -437,13 +463,6 @@ proc mergeShadowScope*(c: PContext) =
|
||||
else:
|
||||
c.addInterfaceDecl(sym)
|
||||
|
||||
when false:
|
||||
# `nimfix` used to call `altSpelling` and prettybase.replaceDeprecated(n.info, ident, alt)
|
||||
proc altSpelling(c: PContext, x: PIdent): PIdent =
|
||||
case x.s[0]
|
||||
of 'A'..'Z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
of 'a'..'z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
else: result = x
|
||||
|
||||
import std/editdistance, heapqueue
|
||||
|
||||
@@ -489,12 +508,9 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||
let e = list.pop()
|
||||
if c.config.spellSuggestMax == spellSuggestSecretSauce:
|
||||
const
|
||||
smallThres = 2
|
||||
maxCountForSmall = 4
|
||||
# avoids ton of operator matches when mis-matching short symbols such as `i`
|
||||
# other heuristics could be devised, such as only suggesting operators if `name0`
|
||||
# is an operator (likewise with non-operators).
|
||||
if e.dist > e0.dist or (name0.len <= smallThres and count >= maxCountForSmall): break
|
||||
minLengthForSuggestion = 4
|
||||
maxCount = 3 # avoids ton of matches; three counts for equal distances
|
||||
if e.dist > e0.dist or count >= maxCount or name0.len < minLengthForSuggestion: break
|
||||
elif count >= c.config.spellSuggestMax: break
|
||||
if count == 0:
|
||||
result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
|
||||
@@ -546,12 +562,20 @@ proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
||||
errorUseQualifier(c, info, candidates, prefix)
|
||||
|
||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
||||
var err = "undeclared identifier: '" & name & "'" & extra
|
||||
if c.recursiveDep.len > 0:
|
||||
err.add "\nThis might be caused by a recursive module dependency:\n"
|
||||
err.add c.recursiveDep
|
||||
# prevent excessive errors for 'nim check'
|
||||
c.recursiveDep = ""
|
||||
var err: string
|
||||
if name == "_":
|
||||
err = "the special identifier '_' is ignored in declarations and cannot be used"
|
||||
else:
|
||||
err = "undeclared identifier: '" & name & "'"
|
||||
if "`gensym" in name:
|
||||
err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym"
|
||||
if extra.len != 0:
|
||||
err.add extra
|
||||
if c.recursiveDep.len > 0:
|
||||
err.add "\nThis might be caused by a recursive module dependency:\n"
|
||||
err.add c.recursiveDep
|
||||
# prevent excessive errors for 'nim check'
|
||||
c.recursiveDep = ""
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
|
||||
@@ -565,13 +589,13 @@ proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
var amb = false
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config)
|
||||
result = searchInScopes(c, n.ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkAccQuoted:
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
else:
|
||||
internalError(c.config, n.info, "lookUp")
|
||||
@@ -593,9 +617,9 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
var amb = false
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
if checkModule in flags:
|
||||
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
|
||||
result = searchInScopes(c, ident, amb)
|
||||
else:
|
||||
let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config)
|
||||
let candidates = searchInScopesFilterBy(c, ident, allExceptModule)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
@@ -627,13 +651,20 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
ident = considerQuotedIdent(c, n[1])
|
||||
if ident != nil:
|
||||
if m == c.module:
|
||||
result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident).skipAlias(n, c.config)
|
||||
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
|
||||
var amb: bool
|
||||
result = errorUseQualifier(c, n.info, m, amb)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], ident)
|
||||
elif n[1].kind == nkSym:
|
||||
result = n[1].sym
|
||||
if result.owner != nil and result.owner != m and checkUndeclared in flags:
|
||||
# dotExpr in templates can end up here
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1]))
|
||||
elif checkUndeclared in flags and
|
||||
n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
|
||||
localError(c.config, n[1].info, "identifier expected, but got: " &
|
||||
@@ -653,7 +684,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
var scope = c.currentScope
|
||||
o.mode = oimNoQualifier
|
||||
while true:
|
||||
result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.it, scope.symbols, ident)
|
||||
if result != nil:
|
||||
o.currentScope = scope
|
||||
break
|
||||
@@ -661,7 +692,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
scope = scope.parent
|
||||
if scope == nil:
|
||||
for i in 0..c.imports.high:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph)
|
||||
if result != nil:
|
||||
o.currentScope = nil
|
||||
o.importIdx = i
|
||||
@@ -684,10 +715,10 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if o.m == c.module:
|
||||
# a module may access its private members:
|
||||
result = initIdentIter(o.it, c.topLevelScope.symbols,
|
||||
ident).skipAlias(n, c.config)
|
||||
ident)
|
||||
o.mode = oimSelfModule
|
||||
else:
|
||||
result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config)
|
||||
result = initModuleIter(o.mit, c.graph, o.m, ident)
|
||||
else:
|
||||
noidentError(c.config, n[1], n)
|
||||
result = errorSym(c, n[1])
|
||||
@@ -720,7 +751,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
|
||||
var idx = o.importIdx+1
|
||||
o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
|
||||
while idx < c.imports.len:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph)
|
||||
if result != nil:
|
||||
# oh, we were wrong, some other module had the symbol, so remember that:
|
||||
o.importIdx = idx
|
||||
@@ -730,7 +761,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
|
||||
proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
|
||||
assert o.currentScope == nil
|
||||
while o.importIdx < c.imports.len:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
|
||||
#while result != nil and result.id in o.marked:
|
||||
# result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
|
||||
if result != nil:
|
||||
@@ -745,29 +776,29 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
of oimNoQualifier:
|
||||
if o.currentScope != nil:
|
||||
assert o.importIdx < 0
|
||||
result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config)
|
||||
result = nextIdentIter(o.it, o.currentScope.symbols)
|
||||
while result == nil:
|
||||
o.currentScope = o.currentScope.parent
|
||||
if o.currentScope != nil:
|
||||
result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.it, o.currentScope.symbols, o.it.name)
|
||||
# BUGFIX: o.it.name <-> n.ident
|
||||
else:
|
||||
o.importIdx = 0
|
||||
if c.imports.len > 0:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
|
||||
if result == nil:
|
||||
result = nextOverloadIterImports(o, c, n)
|
||||
break
|
||||
elif o.importIdx < c.imports.len:
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
|
||||
if result == nil:
|
||||
result = nextOverloadIterImports(o, c, n)
|
||||
else:
|
||||
result = nil
|
||||
of oimSelfModule:
|
||||
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
|
||||
result = nextIdentIter(o.it, c.topLevelScope.symbols)
|
||||
of oimOtherModule:
|
||||
result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config)
|
||||
result = nextModuleIter(o.mit, c.graph)
|
||||
of oimSymChoice:
|
||||
if o.symChoiceIndex < n.len:
|
||||
result = n[o.symChoiceIndex].sym
|
||||
@@ -778,12 +809,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
o.mode = oimSymChoiceLocalLookup
|
||||
o.currentScope = c.currentScope
|
||||
result = firstIdentExcluding(o.it, o.currentScope.symbols,
|
||||
n[0].sym.name, o.marked).skipAlias(n, c.config)
|
||||
n[0].sym.name, o.marked)
|
||||
while result == nil:
|
||||
o.currentScope = o.currentScope.parent
|
||||
if o.currentScope != nil:
|
||||
result = firstIdentExcluding(o.it, o.currentScope.symbols,
|
||||
n[0].sym.name, o.marked).skipAlias(n, c.config)
|
||||
n[0].sym.name, o.marked)
|
||||
else:
|
||||
o.importIdx = 0
|
||||
result = symChoiceExtension(o, c, n)
|
||||
@@ -792,12 +823,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
incl o.marked, result.id
|
||||
of oimSymChoiceLocalLookup:
|
||||
if o.currentScope != nil:
|
||||
result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config)
|
||||
result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked)
|
||||
while result == nil:
|
||||
o.currentScope = o.currentScope.parent
|
||||
if o.currentScope != nil:
|
||||
result = firstIdentExcluding(o.it, o.currentScope.symbols,
|
||||
n[0].sym.name, o.marked).skipAlias(n, c.config)
|
||||
n[0].sym.name, o.marked)
|
||||
else:
|
||||
o.importIdx = 0
|
||||
result = symChoiceExtension(o, c, n)
|
||||
@@ -806,10 +837,10 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
incl o.marked, result.id
|
||||
|
||||
elif o.importIdx < c.imports.len:
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
|
||||
#assert result.id notin o.marked
|
||||
#while result != nil and result.id in o.marked:
|
||||
# result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config)
|
||||
# result = nextIdentIter(o.it, c.imports[o.importIdx])
|
||||
if result == nil:
|
||||
inc o.importIdx
|
||||
result = symChoiceExtension(o, c, n)
|
||||
|
||||
@@ -79,7 +79,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P
|
||||
if avoidTemp:
|
||||
tempAsNode = value
|
||||
else:
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
||||
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)
|
||||
@@ -100,7 +100,7 @@ proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): P
|
||||
## freely, multiple times. This is frequently required and such a builtin would also be
|
||||
## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
|
||||
result = newNodeIT(nkStmtListExpr, value.info, value.typ)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
||||
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)
|
||||
@@ -122,29 +122,10 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
|
||||
proc newTryFinally*(body, final: PNode): PNode =
|
||||
result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
|
||||
|
||||
proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||
vpart[0] = tempAsNode
|
||||
vpart[1] = newNodeI(nkEmpty, value.info)
|
||||
vpart[2] = value
|
||||
v.add vpart
|
||||
result.add(v)
|
||||
|
||||
let lhs = n[0]
|
||||
for i in 0..<lhs.len:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
|
||||
|
||||
proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
|
||||
var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options)
|
||||
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)
|
||||
@@ -171,8 +152,7 @@ proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo
|
||||
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
|
||||
result.n = newNodeI(nkRecList, info)
|
||||
let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
|
||||
nextSymId(idgen),
|
||||
owner, info, owner.options)
|
||||
idgen, owner, info, owner.options)
|
||||
incl s.flags, sfAnon
|
||||
s.typ = result
|
||||
result.sym = s
|
||||
@@ -234,10 +214,13 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym
|
||||
# because of 'gensym' support, we have to mangle the name with its ID.
|
||||
# This is hacky but the clean solution is much more complex than it looks.
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
||||
nextSymId(idgen), s.owner, s.info, s.options)
|
||||
idgen, s.owner, s.info, s.options)
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
field.typ = t
|
||||
if s.kind in {skLet, skVar, skField, skForVar}:
|
||||
#field.bitsize = s.bitsize
|
||||
field.alignment = s.alignment
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = obj.n.len
|
||||
@@ -250,7 +233,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym
|
||||
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
||||
result = lookupInRecord(obj.n, s.itemId)
|
||||
if result == nil:
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen),
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
|
||||
s.owner, s.info, s.options)
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
@@ -347,7 +330,8 @@ proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
||||
|
||||
proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||
info: TLineInfo = unknownLineInfo;
|
||||
arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
|
||||
arg1: PNode = nil, arg2: PNode = nil,
|
||||
arg3: PNode = nil, optionalArgs: PNode = nil): PNode =
|
||||
result = newNodeI(nkCall, info)
|
||||
let sym = magicsys.getCompilerProc(g, name)
|
||||
if sym == nil:
|
||||
|
||||
@@ -26,9 +26,8 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
|
||||
result = systemModuleSym(g, getIdent(g.cache, name))
|
||||
if result == nil:
|
||||
localError(g.config, info, "system module needs: " & name)
|
||||
result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result = newSym(skError, getIdent(g.cache, name), g.idgen, g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
|
||||
if result.kind == skAlias: result = result.owner
|
||||
|
||||
proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
|
||||
let id = getIdent(g.cache, name)
|
||||
@@ -39,7 +38,7 @@ proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSy
|
||||
result = r
|
||||
if result != nil: return result
|
||||
localError(g.config, info, "system module needs: " & name)
|
||||
result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result = newSym(skError, id, g.idgen, g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
|
||||
|
||||
proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
|
||||
|
||||
@@ -13,7 +13,7 @@ when not defined(nimcore):
|
||||
{.error: "nimcore MUST be defined for Nim's core tooling".}
|
||||
|
||||
import
|
||||
std/[strutils, os, times, tables, sha1, with, json],
|
||||
std/[strutils, os, times, tables, with, json],
|
||||
llstream, ast, lexer, syntaxes, options, msgs,
|
||||
condsyms,
|
||||
idents, extccomp,
|
||||
@@ -29,6 +29,8 @@ when defined(nimPreviewSlimSystem):
|
||||
import ic / [cbackend, integrity, navigator]
|
||||
from ic / ic import rodViewer
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
import pipelines
|
||||
|
||||
when not defined(leanCompiler):
|
||||
@@ -149,7 +151,7 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
extccomp.callCCompiler(conf)
|
||||
# for now we do not support writing out a .json file with the build instructions when HCR is on
|
||||
if not conf.hcrOn:
|
||||
extccomp.writeJsonBuildInstructions(conf)
|
||||
extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles)
|
||||
if optGenScript in graph.config.globalOptions:
|
||||
writeDepsFile(graph)
|
||||
if optGenCDeps in graph.config.globalOptions:
|
||||
@@ -415,7 +417,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdJsonscript:
|
||||
setOutFile(graph.config)
|
||||
commandJsonScript(graph)
|
||||
of cmdUnknown, cmdNone, cmdIdeTools, cmdNimfix:
|
||||
of cmdUnknown, cmdNone, cmdIdeTools:
|
||||
rawMessage(conf, errGenerated, "invalid command: " & conf.command)
|
||||
|
||||
if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import intsets, tables, hashes, md5
|
||||
import std/[intsets, tables, hashes, strtabs]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
|
||||
import ic / [packed_ast, ic]
|
||||
|
||||
@@ -56,6 +57,7 @@ type
|
||||
SymInfoPair* = object
|
||||
sym*: PSym
|
||||
info*: TLineInfo
|
||||
isDecl*: bool
|
||||
|
||||
PipelinePass* = enum
|
||||
NonePass
|
||||
@@ -78,6 +80,7 @@ type
|
||||
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
|
||||
attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
|
||||
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
||||
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual and ctor so far)
|
||||
enumToStringProcs*: Table[ItemId, LazySym]
|
||||
emittedTypeInfo*: Table[string, FileIndex]
|
||||
|
||||
@@ -126,6 +129,8 @@ type
|
||||
idgen*: IdGenerator
|
||||
operators*: Operators
|
||||
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
PPassContext* = ref TPassContext
|
||||
@@ -411,7 +416,7 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
|
||||
result = g.doStopCompile != nil and g.doStopCompile()
|
||||
|
||||
proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, name), nextSymId(idgen), nil, unknownLineInfo, {})
|
||||
result = newSym(skProc, getIdent(g.cache, name), idgen, nil, unknownLineInfo, {})
|
||||
result.magic = m
|
||||
result.flags = {sfNeverRaises}
|
||||
|
||||
@@ -477,6 +482,7 @@ proc initModuleGraphFields(result: ModuleGraph) =
|
||||
result.symBodyHashes = initTable[int, SigHash]()
|
||||
result.operators = initOperators(result)
|
||||
result.emittedTypeInfo = initTable[string, FileIndex]()
|
||||
result.cachedFiles = newStringTable()
|
||||
|
||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result = ModuleGraph()
|
||||
|
||||
@@ -36,11 +36,18 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||
localError(n.info, "only '/' supported with $package notation")
|
||||
result = ""
|
||||
else:
|
||||
let modname = getModuleName(conf, n[2])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(conf, n1)
|
||||
result.add renderTree(n0, {renderNoComments}).replace(" ")
|
||||
result.add modname
|
||||
if n0.kind in nkIdentKinds:
|
||||
let ident = n0.getPIdent
|
||||
if ident != nil and ident.s[0] == '/':
|
||||
let modname = getModuleName(conf, n[2])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(conf, n1)
|
||||
result.add renderTree(n0, {renderNoComments}).replace(" ")
|
||||
result.add modname
|
||||
else:
|
||||
result = ""
|
||||
else:
|
||||
result = ""
|
||||
of nkPrefix:
|
||||
when false:
|
||||
if n[0].kind == nkIdent and n[0].ident.s == "$":
|
||||
|
||||
@@ -14,6 +14,9 @@ import
|
||||
idents, lexer, syntaxes, modulegraphs,
|
||||
lineinfos, pathutils
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
import std/strtabs
|
||||
|
||||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||
magicsys.resetSysTypes(g)
|
||||
|
||||
@@ -42,6 +45,8 @@ proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
|
||||
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
|
||||
graph.addDep(s, fileIdx)
|
||||
graph.addIncludeDep(s.position.FileIndex, fileIdx)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
|
||||
proc wantMainModule*(conf: ConfigRef) =
|
||||
if conf.projectFull.isEmpty:
|
||||
|
||||
@@ -226,7 +226,7 @@ proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile)
|
||||
|
||||
proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
|
||||
assert fileIdx.int32 >= 0
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
conf.m.fileInfos[fileIdx.int32].hash = hash
|
||||
else:
|
||||
shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
|
||||
@@ -234,7 +234,7 @@ proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
|
||||
|
||||
proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
assert fileIdx.int32 >= 0
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result = conf.m.fileInfos[fileIdx.int32].hash
|
||||
else:
|
||||
shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
|
||||
@@ -429,7 +429,8 @@ To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 f
|
||||
proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
|
||||
if msg in fatalMsgs:
|
||||
if conf.cmd == cmdIdeTools: log(s)
|
||||
quit(conf, msg)
|
||||
if conf.cmd != cmdIdeTools or msg != errFatal:
|
||||
quit(conf, msg)
|
||||
if msg >= errMin and msg <= errMax or
|
||||
(msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
|
||||
inc(conf.errorCounter)
|
||||
@@ -437,7 +438,11 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string,
|
||||
if conf.errorCounter >= conf.errorMax:
|
||||
# only really quit when we're not in the new 'nim check --def' mode:
|
||||
if conf.ideCmd == ideNone:
|
||||
quit(conf, msg)
|
||||
when defined(nimsuggest):
|
||||
#we need to inform the user that something went wrong when initializing NimSuggest
|
||||
raiseRecoverableError(s)
|
||||
else:
|
||||
quit(conf, msg)
|
||||
elif eh == doAbort and conf.cmd != cmdIdeTools:
|
||||
quit(conf, msg)
|
||||
elif eh == doRaise:
|
||||
@@ -511,7 +516,8 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
|
||||
conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
|
||||
|
||||
proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.gcsafe, noinline.} =
|
||||
eh: TErrorHandling, info2: InstantiationInfo, isRaw = false,
|
||||
ignoreError = false) {.gcsafe, noinline.} =
|
||||
var
|
||||
title: string
|
||||
color: ForegroundColor
|
||||
@@ -552,9 +558,10 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
ignoreMsg = not conf.hasHint(msg)
|
||||
if not ignoreMsg and msg in conf.warningAsErrors:
|
||||
title = ErrorTitle
|
||||
color = ErrorColor
|
||||
else:
|
||||
title = HintTitle
|
||||
color = HintColor
|
||||
color = HintColor
|
||||
inc(conf.hintCounter)
|
||||
|
||||
let s = if isRaw: arg else: getMessageStr(msg, arg)
|
||||
@@ -576,7 +583,8 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
" compiler msg initiated here", KindColor,
|
||||
KindFormat % $hintMsgOrigin,
|
||||
resetStyle, conf.unitSep)
|
||||
handleError(conf, msg, eh, s, ignoreMsg)
|
||||
if not ignoreError:
|
||||
handleError(conf, msg, eh, s, ignoreMsg)
|
||||
if msg in fatalMsgs:
|
||||
# most likely would have died here but just in case, we restore state
|
||||
conf.m.errorOutputs = errorOutputsOld
|
||||
@@ -641,13 +649,16 @@ template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraM
|
||||
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
|
||||
liMessage(conf, info, msg, m, doNothing, instLoc())
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
||||
if i.fileIndex.int32 < 0:
|
||||
proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
|
||||
if fi.int32 < 0:
|
||||
result = makeCString "???"
|
||||
elif optExcessiveStackTrace in conf.globalOptions:
|
||||
result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
|
||||
result = conf.m.fileInfos[fi.int32].quotedFullName
|
||||
else:
|
||||
result = conf.m.fileInfos[i.fileIndex.int32].quotedName
|
||||
result = conf.m.fileInfos[fi.int32].quotedName
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
||||
quotedFilename(conf, i.fileIndex)
|
||||
|
||||
template listMsg(title, r) =
|
||||
msgWriteln(conf, title, {msgNoUnitSep})
|
||||
|
||||
@@ -909,7 +909,7 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
|
||||
else: ""
|
||||
|
||||
var cache = newIdentCache()
|
||||
var op = newSym(skVar, cache.getIdent(name), nextSymId ctx.idgen, nil, r.info)
|
||||
var op = newSym(skVar, cache.getIdent(name), ctx.idgen, nil, r.info)
|
||||
|
||||
op.magic = magic
|
||||
result = nkInfix.newTree(
|
||||
@@ -920,7 +920,7 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
|
||||
|
||||
proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
|
||||
var cache = newIdentCache()
|
||||
var op = newSym(skVar, cache.getIdent("not"), nextSymId ctx.idgen, nil, node.info)
|
||||
var op = newSym(skVar, cache.getIdent("not"), ctx.idgen, nil, node.info)
|
||||
|
||||
op.magic = mNot
|
||||
result = nkPrefix.newTree(
|
||||
|
||||
@@ -7,6 +7,7 @@ define:nimcore
|
||||
define:nimPreviewFloatRoundtrip
|
||||
define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
threads:off
|
||||
|
||||
@@ -14,7 +15,6 @@ threads:off
|
||||
|
||||
@if windows:
|
||||
cincludes: "$lib/wrappers/libffi/common"
|
||||
tlsEmulation:off
|
||||
@end
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
@@ -31,9 +31,6 @@ define:useStdoutAsStdmsg
|
||||
warning[ObservableStores]:off
|
||||
@end
|
||||
|
||||
@if nimHasWarnCastSizes:
|
||||
warning[CastSizes]:on
|
||||
@end
|
||||
|
||||
@if nimHasWarningAsError:
|
||||
warningAsError[GcUnsafe2]:on
|
||||
|
||||
@@ -12,10 +12,7 @@ import std/[os, strutils, parseopt]
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
when defined(windows) and not defined(nimKochBootstrap):
|
||||
# remove workaround pending bootstrap >= 1.5.1
|
||||
# refs https://github.com/nim-lang/Nim/issues/18334#issuecomment-867114536
|
||||
# alternative would be to prepend `currentSourcePath.parentDir.quoteShell`
|
||||
when defined(windows):
|
||||
when defined(gcc):
|
||||
when defined(x86):
|
||||
{.link: "../icons/nim.res".}
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
## Implements some helper procs for Nimble (Nim's package manager) support.
|
||||
|
||||
import parseutils, strutils, os, options, msgs, sequtils, lineinfos, pathutils,
|
||||
std/sha1, tables
|
||||
tables
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
|
||||
if not conf.searchPaths.contains(path):
|
||||
conf.searchPaths.insert(path, 0)
|
||||
|
||||
@@ -12,7 +12,7 @@ import
|
||||
ast, modules, condsyms,
|
||||
options, llstream, lineinfos, vm,
|
||||
vmdef, modulegraphs, idents, os, pathutils,
|
||||
scriptconfig, std/compilesettings
|
||||
scriptconfig, std/[compilesettings, tables]
|
||||
|
||||
import pipelines
|
||||
|
||||
@@ -78,6 +78,9 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
|
||||
assert i != nil
|
||||
assert i.mainModule != nil, "no main module selected"
|
||||
initStrTables(i.graph, i.mainModule)
|
||||
i.graph.cacheSeqs.clear()
|
||||
i.graph.cacheCounters.clear()
|
||||
i.graph.cacheTables.clear()
|
||||
i.mainModule.ast = nil
|
||||
|
||||
let s = if scriptStream != nil: scriptStream
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
|
||||
|
||||
import strutils, os, parseopt
|
||||
import compiler/[options, commands, modules, sem,
|
||||
passes, passaux, linter,
|
||||
msgs, nimconf,
|
||||
extccomp, condsyms,
|
||||
modulegraphs, idents]
|
||||
|
||||
const Usage = """
|
||||
Nimfix - Tool to patch Nim code
|
||||
Usage:
|
||||
nimfix [options] projectfile.nim
|
||||
|
||||
Options:
|
||||
--overwriteFiles:on|off overwrite the original nim files.
|
||||
DEFAULT is ON!
|
||||
--wholeProject overwrite every processed file.
|
||||
--checkExtern:on|off style check also extern names
|
||||
--styleCheck:on|off|auto performs style checking for identifiers
|
||||
and suggests an alternative spelling;
|
||||
'auto' corrects the spelling.
|
||||
--bestEffort try to fix the code even when there
|
||||
are errors.
|
||||
|
||||
In addition, all command line options of Nim are supported.
|
||||
"""
|
||||
|
||||
proc mainCommand =
|
||||
registerPass verbosePass
|
||||
registerPass semPass
|
||||
conf.setCmd cmdNimfix
|
||||
searchPaths.add options.libpath
|
||||
if gProjectFull.len != 0:
|
||||
# current path is always looked first for modules
|
||||
searchPaths.insert(gProjectPath, 0)
|
||||
|
||||
compileProject(newModuleGraph(), newIdentCache())
|
||||
pretty.overwriteFiles()
|
||||
|
||||
proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
var argsCount = 0
|
||||
gOnlyMainfile = true
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
case p.key.normalize
|
||||
of "overwritefiles":
|
||||
case p.val.normalize
|
||||
of "on": gOverWrite = true
|
||||
of "off": gOverWrite = false
|
||||
else: localError(gCmdLineInfo, errOnOrOffExpected)
|
||||
of "checkextern":
|
||||
case p.val.normalize
|
||||
of "on": gCheckExtern = true
|
||||
of "off": gCheckExtern = false
|
||||
else: localError(gCmdLineInfo, errOnOrOffExpected)
|
||||
of "stylecheck":
|
||||
case p.val.normalize
|
||||
of "off": gStyleCheck = StyleCheck.None
|
||||
of "on": gStyleCheck = StyleCheck.Warn
|
||||
of "auto": gStyleCheck = StyleCheck.Auto
|
||||
else: localError(gCmdLineInfo, errOnOrOffExpected)
|
||||
of "wholeproject": gOnlyMainfile = false
|
||||
of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
|
||||
else:
|
||||
processSwitch(pass, p, config)
|
||||
of cmdArgument:
|
||||
options.gProjectName = unixToNativePath(p.key)
|
||||
# if processArgument(pass, p, argsCount): break
|
||||
|
||||
proc handleCmdLine(config: ConfigRef) =
|
||||
if paramCount() == 0:
|
||||
stdout.writeLine(Usage)
|
||||
else:
|
||||
processCmdLine(passCmd1, "", config)
|
||||
if gProjectName != "":
|
||||
try:
|
||||
gProjectFull = canonicalizePath(gProjectName)
|
||||
except OSError:
|
||||
gProjectFull = gProjectName
|
||||
var p = splitFile(gProjectFull)
|
||||
gProjectPath = p.dir
|
||||
gProjectName = p.name
|
||||
else:
|
||||
gProjectPath = getCurrentDir()
|
||||
loadConfigs(DefaultConfig, config) # load all config files
|
||||
# now process command line arguments again, because some options in the
|
||||
# command line can overwrite the config file's settings
|
||||
extccomp.initVars()
|
||||
processCmdLine(passCmd2, "", config)
|
||||
mainCommand()
|
||||
|
||||
when compileOption("gc", "refc"):
|
||||
GC_disableMarkAndSweep()
|
||||
|
||||
condsyms.initDefines()
|
||||
defineSymbol "nimfix"
|
||||
handleCmdline newConfigRef()
|
||||
@@ -1,17 +0,0 @@
|
||||
# Special configuration file for the Nim project
|
||||
# gc:markAndSweep
|
||||
|
||||
hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"$nim"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
symbol:nimfix
|
||||
define:nimfix
|
||||
|
||||
cs:partial
|
||||
#define:useNodeIds
|
||||
define:booting
|
||||
define:noDocgen
|
||||
@@ -1,45 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import strutils except Letters
|
||||
import ".." / [ast, msgs, lineinfos, idents, options, linter]
|
||||
|
||||
proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
|
||||
let line = sourceLine(conf, info)
|
||||
var first = min(info.col.int, line.len)
|
||||
if first < 0: return
|
||||
#inc first, skipIgnoreCase(line, "proc ", first)
|
||||
while first > 0 and line[first-1] in Letters: dec first
|
||||
if first < 0: return
|
||||
if line[first] == '`': inc first
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
|
||||
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
|
||||
else:
|
||||
system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
|
||||
conf.m.fileInfos[info.fileIndex.int32].dirty = true
|
||||
#if newSym.s == "File": writeStackTrace()
|
||||
|
||||
proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) =
|
||||
replaceDeprecated(conf, info, oldSym.name, newSym.name)
|
||||
|
||||
proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
|
||||
let line = sourceLine(conf, info)
|
||||
var first = info.col.int
|
||||
if line[first] != '#': inc first
|
||||
|
||||
var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
|
||||
else:
|
||||
system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
|
||||
conf.m.fileInfos[info.fileIndex.int32].dirty = true
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
const
|
||||
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
|
||||
DefaultSetElements* = 1 shl 8
|
||||
## assumed set element count when using int literals
|
||||
VersionAsString* = system.NimVersion
|
||||
RodFileVersion* = "1223" # modify this if the rod-format changes!
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
|
||||
if n[0].kind == nkSym:
|
||||
let s = n[0].sym
|
||||
let name = s.name.s.normalize
|
||||
if s.magic == mWasMoved or name == "=wasmoved":
|
||||
if name == "=wasmoved":
|
||||
b.wasMovedLocs.add n
|
||||
special = true
|
||||
elif name == "=destroy":
|
||||
|
||||
@@ -49,6 +49,7 @@ type # please make sure we have under 32 options
|
||||
optSinkInference # 'sink T' inference
|
||||
optCursorInference
|
||||
optImportHidden
|
||||
optQuirky
|
||||
|
||||
TOptions* = set[TOption]
|
||||
TGlobalOption* = enum
|
||||
@@ -102,13 +103,11 @@ type # please make sure we have under 32 options
|
||||
optBenchmarkVM # Enables cpuTime() in the VM
|
||||
optProduceAsm # produce assembler code
|
||||
optPanics # turn panics (sysFatal) into a process termination
|
||||
optNimV1Emulation # emulate Nim v1.0
|
||||
optNimV12Emulation # emulate Nim v1.2
|
||||
optNimV16Emulation # emulate Nim v1.6
|
||||
optSourcemap
|
||||
optProfileVM # enable VM profiler
|
||||
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
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
@@ -168,7 +167,6 @@ type
|
||||
cmdInteractive # start interactive session
|
||||
cmdNop
|
||||
cmdJsonscript # compile a .json build file
|
||||
cmdNimfix
|
||||
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
|
||||
|
||||
const
|
||||
@@ -186,6 +184,7 @@ type
|
||||
gcRegions = "regions"
|
||||
gcArc = "arc"
|
||||
gcOrc = "orc"
|
||||
gcAtomicArc = "atomicArc"
|
||||
gcMarkAndSweep = "markAndSweep"
|
||||
gcHooks = "hooks"
|
||||
gcRefc = "refc"
|
||||
@@ -196,7 +195,7 @@ type
|
||||
IdeCmd* = enum
|
||||
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
|
||||
ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
|
||||
ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand
|
||||
ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand, ideInlayHints
|
||||
|
||||
Feature* = enum ## experimental features; DO NOT RENAME THESE!
|
||||
dotOperators,
|
||||
@@ -221,7 +220,8 @@ type
|
||||
unicodeOperators, # deadcode
|
||||
flexibleOptionalParams,
|
||||
strictDefs,
|
||||
strictCaseObjects
|
||||
strictCaseObjects,
|
||||
genericsOpenSym
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
@@ -281,9 +281,24 @@ type
|
||||
version*: int
|
||||
endLine*: uint16
|
||||
endCol*: int
|
||||
inlayHintInfo*: SuggestInlayHint
|
||||
|
||||
Suggestions* = seq[Suggest]
|
||||
|
||||
SuggestInlayHintKind* = enum
|
||||
sihkType = "Type",
|
||||
sihkParameter = "Parameter"
|
||||
|
||||
SuggestInlayHint* = ref object
|
||||
kind*: SuggestInlayHintKind
|
||||
line*: int # Starts at 1
|
||||
column*: int # Starts at 0
|
||||
label*: string
|
||||
paddingLeft*: bool
|
||||
paddingRight*: bool
|
||||
allowInsert*: bool
|
||||
tooltip*: string
|
||||
|
||||
ProfileInfo* = object
|
||||
time*: float
|
||||
count*: int
|
||||
@@ -335,6 +350,7 @@ type
|
||||
|
||||
cppDefines*: HashSet[string] # (*)
|
||||
headerFile*: string
|
||||
nimbasePattern*: string # pattern to find nimbase.h
|
||||
features*: set[Feature]
|
||||
legacyFeatures*: set[LegacyFeature]
|
||||
arguments*: string ## the arguments to be passed to the program that
|
||||
@@ -417,6 +433,9 @@ type
|
||||
expandNodeResult*: string
|
||||
expandPosition*: TLineInfo
|
||||
|
||||
clientProcessId*: int
|
||||
|
||||
|
||||
proc parseNimVersion*(a: string): NimVer =
|
||||
# could be moved somewhere reusable
|
||||
if a.len > 0:
|
||||
@@ -479,7 +498,8 @@ const
|
||||
optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
|
||||
optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
|
||||
optTrMacros, optStyleCheck, optCursorInference}
|
||||
DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace}
|
||||
DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace,
|
||||
optJsBigInt64}
|
||||
|
||||
proc getSrcTimestamp(): DateTime =
|
||||
try:
|
||||
@@ -866,14 +886,6 @@ template patchModule(conf: ConfigRef) {.dirty.} =
|
||||
let ov = conf.moduleOverrides[key]
|
||||
if ov.len > 0: result = AbsoluteFile(ov)
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
|
||||
proc isRelativeTo(path, base: string): bool =
|
||||
# pending #13212 use os.isRelativeTo
|
||||
let path = path.normalizedPath
|
||||
let base = base.normalizedPath
|
||||
let ret = relativePath(path, base)
|
||||
result = path.len > 0 and not ret.startsWith ".."
|
||||
|
||||
const stdlibDirs* = [
|
||||
"pure", "core", "arch",
|
||||
"pure/collections",
|
||||
@@ -918,6 +930,7 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile
|
||||
proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
|
||||
# returns path to module
|
||||
var m = addFileExt(modulename, NimExt)
|
||||
var hasRelativeDot = false
|
||||
if m.startsWith(pkgPrefix):
|
||||
result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
|
||||
else:
|
||||
@@ -931,7 +944,11 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi
|
||||
else: # If prefixed with std/ why would we add the current module path!
|
||||
let currentPath = currentModule.splitFile.dir
|
||||
result = AbsoluteFile currentPath / m
|
||||
if not fileExists(result):
|
||||
if m.startsWith('.') and not fileExists(result):
|
||||
result = AbsoluteFile ""
|
||||
hasRelativeDot = true
|
||||
|
||||
if not fileExists(result) and not hasRelativeDot:
|
||||
result = findFile(conf, m)
|
||||
patchModule(conf)
|
||||
|
||||
@@ -1058,6 +1075,7 @@ proc `$`*(c: IdeCmd): string =
|
||||
of ideRecompile: "recompile"
|
||||
of ideChanged: "changed"
|
||||
of ideType: "type"
|
||||
of ideInlayHints: "inlayHints"
|
||||
|
||||
proc floatInt64Align*(conf: ConfigRef): int16 =
|
||||
## Returns either 4 or 8 depending on reasons.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
## Package related procs.
|
||||
##
|
||||
##
|
||||
## See Also:
|
||||
## * `packagehandling` for package path handling
|
||||
## * `modulegraphs.getPackage`
|
||||
@@ -22,7 +22,7 @@ when defined(nimPreviewSlimSystem):
|
||||
|
||||
proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
|
||||
## Return a new package symbol.
|
||||
##
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.getPackage`
|
||||
let
|
||||
@@ -31,7 +31,7 @@ proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
|
||||
info = newLineInfo(fileIdx, 1, 1)
|
||||
pkgName = getPackageName(conf, filename.string)
|
||||
pkgIdent = getIdent(cache, pkgName)
|
||||
newSym(skPackage, pkgIdent, ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info)
|
||||
newSym(skPackage, pkgIdent, idGeneratorForPackage(int32(fileIdx)), nil, info)
|
||||
|
||||
func getPackageSymbol*(sym: PSym): PSym =
|
||||
## Return the owning package symbol.
|
||||
@@ -47,7 +47,7 @@ func getPackageId*(sym: PSym): int =
|
||||
|
||||
func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
|
||||
## Return whether the symbol belongs to the project's package.
|
||||
##
|
||||
##
|
||||
## See Also:
|
||||
## * `modulegraphs.belongsToStdlib`
|
||||
conf.mainPackageId == sym.getPackageId
|
||||
|
||||
@@ -301,17 +301,16 @@ proc isRightAssociative(tok: Token): bool {.inline.} =
|
||||
proc isUnary(tok: Token): bool =
|
||||
## Check if the given token is a unary operator
|
||||
tok.tokType in {tkOpr, tkDotDot} and
|
||||
tok.strongSpaceB == tsNone and
|
||||
tok.strongSpaceA
|
||||
tok.spacing == {tsLeading}
|
||||
|
||||
proc checkBinary(p: Parser) {.inline.} =
|
||||
## Check if the current parser token is a binary operator.
|
||||
# we don't check '..' here as that's too annoying
|
||||
if p.tok.tokType == tkOpr:
|
||||
if p.tok.strongSpaceB == tsTrailing and not p.tok.strongSpaceA:
|
||||
if p.tok.spacing == {tsTrailing}:
|
||||
parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
|
||||
|
||||
#| module = stmt ^* (';' / IND{=})
|
||||
#| module = complexOrSimpleStmt ^* (';' / IND{=})
|
||||
#|
|
||||
#| comma = ',' COMMENT?
|
||||
#| semicolon = ';' COMMENT?
|
||||
@@ -321,7 +320,7 @@ proc checkBinary(p: Parser) {.inline.} =
|
||||
#| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
|
||||
#| | 'or' | 'xor' | 'and'
|
||||
#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
|
||||
#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
|
||||
#| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | '..'
|
||||
#|
|
||||
#| prefixOperator = operator
|
||||
#|
|
||||
@@ -362,7 +361,8 @@ template setEndInfo() =
|
||||
|
||||
proc parseSymbol(p: var Parser, mode = smNormal): PNode =
|
||||
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
|
||||
#| | IDENT | KEYW
|
||||
#| | IDENT | 'addr' | 'type' | 'static'
|
||||
#| symbolOrKeyword = symbol | KEYW
|
||||
case p.tok.tokType
|
||||
of tkSymbol:
|
||||
result = newIdentNodeP(p.tok.ident, p)
|
||||
@@ -436,7 +436,8 @@ proc colonOrEquals(p: var Parser, a: PNode): PNode =
|
||||
result = equals(p, a)
|
||||
|
||||
proc exprColonEqExpr(p: var Parser): PNode =
|
||||
#| exprColonEqExpr = expr (':'|'=' expr)?
|
||||
#| exprColonEqExpr = expr ((':'|'=') expr
|
||||
#| / doBlock extraPostExprBlock*)?
|
||||
var a = parseExpr(p)
|
||||
if p.tok.tokType == tkDo:
|
||||
result = postExprBlocks(p, a)
|
||||
@@ -444,7 +445,8 @@ proc exprColonEqExpr(p: var Parser): PNode =
|
||||
result = colonOrEquals(p, a)
|
||||
|
||||
proc exprEqExpr(p: var Parser): PNode =
|
||||
#| exprEqExpr = expr ('=' expr)?
|
||||
#| exprEqExpr = expr ('=' expr
|
||||
#| / doBlock extraPostExprBlock*)?
|
||||
var a = parseExpr(p)
|
||||
if p.tok.tokType == tkDo:
|
||||
result = postExprBlocks(p, a)
|
||||
@@ -515,7 +517,7 @@ proc dotExpr(p: var Parser, a: PNode): PNode =
|
||||
optInd(p, result)
|
||||
result.add(a)
|
||||
result.add(parseSymbol(p, smAfterDot))
|
||||
if p.tok.tokType == tkBracketLeColon and not p.tok.strongSpaceA:
|
||||
if p.tok.tokType == tkBracketLeColon and tsLeading notin p.tok.spacing:
|
||||
var x = newNodeI(nkBracketExpr, p.parLineInfo)
|
||||
# rewrite 'x.y[:z]()' to 'y[z](x)'
|
||||
x.add result[1]
|
||||
@@ -524,7 +526,7 @@ proc dotExpr(p: var Parser, a: PNode): PNode =
|
||||
var y = newNodeI(nkCall, p.parLineInfo)
|
||||
y.add x
|
||||
y.add result[0]
|
||||
if p.tok.tokType == tkParLe and not p.tok.strongSpaceA:
|
||||
if p.tok.tokType == tkParLe and tsLeading notin p.tok.spacing:
|
||||
exprColonEqExprListAux(p, tkParRi, y)
|
||||
result = y
|
||||
|
||||
@@ -539,7 +541,7 @@ proc dotLikeExpr(p: var Parser, a: PNode): PNode =
|
||||
result.add(parseSymbol(p, smAfterDot))
|
||||
|
||||
proc qualifiedIdent(p: var Parser): PNode =
|
||||
#| qualifiedIdent = symbol ('.' optInd symbol)?
|
||||
#| qualifiedIdent = symbol ('.' optInd symbolOrKeyword)?
|
||||
result = parseSymbol(p)
|
||||
if p.tok.tokType == tkDot: result = dotExpr(p, result)
|
||||
|
||||
@@ -647,7 +649,8 @@ proc parsePar(p: var Parser): PNode =
|
||||
#| ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
|
||||
#| | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
|
||||
#| | pragmaStmt
|
||||
#| | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
|
||||
#| | simpleExpr ( (doBlock extraPostExprBlock*)
|
||||
#| | ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
|
||||
#| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
|
||||
#| optPar ')'
|
||||
#
|
||||
@@ -869,8 +872,8 @@ proc isDotLike(tok: Token): bool =
|
||||
proc primarySuffix(p: var Parser, r: PNode,
|
||||
baseIndent: int, mode: PrimaryMode): PNode =
|
||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')'
|
||||
#| | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
|
||||
#| | DOTLIKEOP optInd symbol generalizedLit?
|
||||
#| | '.' optInd symbolOrKeyword ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
|
||||
#| | DOTLIKEOP optInd symbolOrKeyword generalizedLit?
|
||||
#| | '[' optInd exprColonEqExprList optPar ']'
|
||||
#| | '{' optInd exprColonEqExprList optPar '}'
|
||||
# XXX strong spaces need to be reflected above
|
||||
@@ -882,7 +885,7 @@ proc primarySuffix(p: var Parser, r: PNode,
|
||||
case p.tok.tokType
|
||||
of tkParLe:
|
||||
# progress guaranteed
|
||||
if p.tok.strongSpaceA:
|
||||
if tsLeading in p.tok.spacing:
|
||||
result = commandExpr(p, result, mode)
|
||||
break
|
||||
result = namedParams(p, result, nkCall, tkParRi)
|
||||
@@ -894,13 +897,13 @@ proc primarySuffix(p: var Parser, r: PNode,
|
||||
result = parseGStrLit(p, result)
|
||||
of tkBracketLe:
|
||||
# progress guaranteed
|
||||
if p.tok.strongSpaceA:
|
||||
if tsLeading in p.tok.spacing:
|
||||
result = commandExpr(p, result, mode)
|
||||
break
|
||||
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
|
||||
of tkCurlyLe:
|
||||
# progress guaranteed
|
||||
if p.tok.strongSpaceA:
|
||||
if tsLeading in p.tok.spacing:
|
||||
result = commandExpr(p, result, mode)
|
||||
break
|
||||
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
||||
@@ -1005,7 +1008,7 @@ proc parsePragma(p: var Parser): PNode =
|
||||
|
||||
proc identVis(p: var Parser; allowDot=false): PNode =
|
||||
#| identVis = symbol OPR? # postfix position
|
||||
#| identVisDot = symbol '.' optInd symbol OPR?
|
||||
#| identVisDot = symbol '.' optInd symbolOrKeyword OPR?
|
||||
var a = parseSymbol(p)
|
||||
if p.tok.tokType == tkOpr:
|
||||
when defined(nimpretty):
|
||||
@@ -1197,14 +1200,18 @@ proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
|
||||
let pragmas = optPragmas(p)
|
||||
if p.tok.tokType == tkEquals and isExpr:
|
||||
getTok(p)
|
||||
skipComment(p, result)
|
||||
result = newProcNode(kind, info, body = parseStmt(p),
|
||||
result = newProcNode(kind, info, body = p.emptyNode,
|
||||
params = params, name = p.emptyNode, pattern = p.emptyNode,
|
||||
genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
|
||||
skipComment(p, result)
|
||||
result[bodyPos] = parseStmt(p)
|
||||
else:
|
||||
result = newNodeI(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info)
|
||||
if hasSignature:
|
||||
result.add(params)
|
||||
if hasSignature or pragmas.kind != nkEmpty:
|
||||
if hasSignature:
|
||||
result.add(params)
|
||||
else: # pragmas but no param list, implies typeclass with pragmas
|
||||
result.add(p.emptyNode)
|
||||
if kind == nkFuncDef:
|
||||
parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead")
|
||||
result.add(pragmas)
|
||||
@@ -1332,7 +1339,7 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
|
||||
#| simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix*
|
||||
#| commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'|
|
||||
#| 'static'|'enum'|'tuple'|'object'|'proc')
|
||||
#| primary = simplePrimary (commandStart expr)
|
||||
#| primary = simplePrimary (commandStart expr (doBlock extraPostExprBlock*)?)?
|
||||
#| / operatorB primary
|
||||
#| / routineExpr
|
||||
#| / rawTypeDesc
|
||||
@@ -1389,7 +1396,7 @@ proc binaryNot(p: var Parser; a: PNode): PNode =
|
||||
let notOpr = newIdentNodeP(p.tok.ident, p)
|
||||
getTok(p)
|
||||
optInd(p, notOpr)
|
||||
let b = parseExpr(p)
|
||||
let b = primary(p, pmTypeDesc)
|
||||
result = newNodeP(nkInfix, p)
|
||||
result.add notOpr
|
||||
result.add a
|
||||
@@ -1400,8 +1407,8 @@ proc binaryNot(p: var Parser; a: PNode): PNode =
|
||||
proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
|
||||
#| rawTypeDesc = (tupleType | routineType | 'enum' | 'object' |
|
||||
#| ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?)
|
||||
#| ('not' expr)?
|
||||
#| typeDescExpr = (routineType / simpleExpr) ('not' expr)?
|
||||
#| ('not' primary)?
|
||||
#| typeDescExpr = (routineType / simpleExpr) ('not' primary)?
|
||||
#| typeDesc = rawTypeDesc / typeDescExpr
|
||||
newlineWasSplitting(p)
|
||||
if fullExpr:
|
||||
@@ -1423,6 +1430,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
|
||||
result = newNodeP(nkObjectTy, p)
|
||||
getTok(p)
|
||||
of tkConcept:
|
||||
result = p.emptyNode
|
||||
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
|
||||
of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc)
|
||||
of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc)
|
||||
@@ -1437,8 +1445,8 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
|
||||
proc parseTypeDefValue(p: var Parser): PNode =
|
||||
#| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
|
||||
#| ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl))
|
||||
#| / (simpleExpr (exprEqExpr ^+ comma postExprBlocks)?))
|
||||
#| ('not' expr)?
|
||||
#| / (simpleExpr (exprEqExpr ^+ comma postExprBlocks?)?))
|
||||
#| ('not' primary)?
|
||||
case p.tok.tokType
|
||||
of tkTuple: result = parseTuple(p, true)
|
||||
of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDef)
|
||||
@@ -1474,12 +1482,13 @@ proc makeCall(n: PNode): PNode =
|
||||
result.add n
|
||||
|
||||
proc postExprBlocks(p: var Parser, x: PNode): PNode =
|
||||
#| postExprBlocks = ':' stmt? ( IND{=} doBlock
|
||||
#| | IND{=} 'of' exprList ':' stmt
|
||||
#| | IND{=} 'elif' expr ':' stmt
|
||||
#| | IND{=} 'except' optionalExprList ':' stmt
|
||||
#| | IND{=} 'finally' ':' stmt
|
||||
#| | IND{=} 'else' ':' stmt )*
|
||||
#| extraPostExprBlock = ( IND{=} doBlock
|
||||
#| | IND{=} 'of' exprList ':' stmt
|
||||
#| | IND{=} 'elif' expr ':' stmt
|
||||
#| | IND{=} 'except' optionalExprList ':' stmt
|
||||
#| | IND{=} 'finally' ':' stmt
|
||||
#| | IND{=} 'else' ':' stmt )
|
||||
#| postExprBlocks = (doBlock / ':' (extraPostExprBlock / stmt)) extraPostExprBlock*
|
||||
result = x
|
||||
if p.tok.indent >= 0: return
|
||||
|
||||
@@ -1496,7 +1505,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
|
||||
result = makeCall(result)
|
||||
getTok(p)
|
||||
skipComment(p, result)
|
||||
if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept, tkFinally}:
|
||||
if not (p.tok.tokType in {tkOf, tkElif, tkElse, tkExcept, tkFinally} and sameInd(p)):
|
||||
var stmtList = newNodeP(nkStmtList, p)
|
||||
stmtList.add parseStmt(p)
|
||||
# to keep backwards compatibility (see tests/vm/tstringnil)
|
||||
@@ -1713,9 +1722,9 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
|
||||
setEndInfo()
|
||||
|
||||
proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
|
||||
#| condExpr = expr colcom expr optInd
|
||||
#| ('elif' expr colcom expr optInd)*
|
||||
#| 'else' colcom expr
|
||||
#| condExpr = expr colcom stmt optInd
|
||||
#| ('elif' expr colcom stmt optInd)*
|
||||
#| 'else' colcom stmt
|
||||
#| ifExpr = 'if' condExpr
|
||||
#| whenExpr = 'when' condExpr
|
||||
result = newNodeP(kind, p)
|
||||
@@ -2277,20 +2286,31 @@ proc parseTypeDef(p: var Parser): PNode =
|
||||
setEndInfo()
|
||||
|
||||
proc parseVarTuple(p: var Parser): PNode =
|
||||
#| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
|
||||
#| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
|
||||
#| varTuple = varTupleLhs '=' optInd expr
|
||||
result = newNodeP(nkVarTuple, p)
|
||||
getTok(p) # skip '('
|
||||
optInd(p, result)
|
||||
# progress guaranteed
|
||||
while p.tok.tokType in {tkSymbol, tkAccent}:
|
||||
var a = identWithPragma(p, allowDot=true)
|
||||
while p.tok.tokType in {tkSymbol, tkAccent, tkParLe}:
|
||||
var a: PNode
|
||||
if p.tok.tokType == tkParLe:
|
||||
a = parseVarTuple(p)
|
||||
a.add(p.emptyNode)
|
||||
else:
|
||||
a = identWithPragma(p, allowDot=true)
|
||||
result.add(a)
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
skipComment(p, a)
|
||||
result.add(p.emptyNode) # no type desc
|
||||
optPar(p)
|
||||
eat(p, tkParRi)
|
||||
if p.tok.tokType == tkColon:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
result.add(parseTypeDesc(p, fullExpr = true))
|
||||
else:
|
||||
result.add(p.emptyNode) # no type desc
|
||||
setEndInfo()
|
||||
|
||||
proc parseVariable(p: var Parser): PNode =
|
||||
@@ -2497,24 +2517,8 @@ proc parseStmt(p: var Parser): PNode =
|
||||
if err and p.tok.tokType == tkEof: break
|
||||
setEndInfo()
|
||||
|
||||
proc parseAll(p: var Parser): PNode =
|
||||
## Parses the rest of the input stream held by the parser into a PNode.
|
||||
result = newNodeP(nkStmtList, p)
|
||||
while p.tok.tokType != tkEof:
|
||||
p.hasProgress = false
|
||||
var a = complexOrSimpleStmt(p)
|
||||
if a.kind != nkEmpty and p.hasProgress:
|
||||
result.add(a)
|
||||
else:
|
||||
parMessage(p, errExprExpected, p.tok)
|
||||
# bugfix: consume a token here to prevent an endless loop:
|
||||
getTok(p)
|
||||
if p.tok.indent != 0:
|
||||
parMessage(p, errInvalidIndentation)
|
||||
setEndInfo()
|
||||
|
||||
proc checkFirstLineIndentation*(p: var Parser) =
|
||||
if p.tok.indent != 0 and p.tok.strongSpaceA:
|
||||
if p.tok.indent != 0 and tsLeading in p.tok.spacing:
|
||||
parMessage(p, errInvalidIndentation)
|
||||
|
||||
proc parseTopLevelStmt(p: var Parser): PNode =
|
||||
@@ -2547,6 +2551,16 @@ proc parseTopLevelStmt(p: var Parser): PNode =
|
||||
break
|
||||
setEndInfo()
|
||||
|
||||
proc parseAll*(p: var Parser): PNode =
|
||||
## Parses the rest of the input stream held by the parser into a PNode.
|
||||
result = newNodeP(nkStmtList, p)
|
||||
while true:
|
||||
let nextStmt = p.parseTopLevelStmt()
|
||||
if nextStmt.kind == nkEmpty:
|
||||
break
|
||||
result &= nextStmt
|
||||
setEndInfo()
|
||||
|
||||
proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
|
||||
filename: string = ""; line: int = 0;
|
||||
errorHandler: ErrorHandler = nil): PNode =
|
||||
|
||||
@@ -24,7 +24,7 @@ import ic/replayer
|
||||
export skipCodegen, resolveMod, prepareConfigNotes
|
||||
|
||||
when defined(nimsuggest):
|
||||
import std/sha1
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
|
||||
lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
|
||||
packages, syntaxes, depends, vm, pragmas, idents, lookups
|
||||
packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
|
||||
liftdestructors
|
||||
|
||||
import pipelineutils
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import jsgen, docgen2
|
||||
|
||||
import std/[syncio, objectdollar, assertions, tables, strutils]
|
||||
import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
|
||||
import renderer
|
||||
import ic/replayer
|
||||
|
||||
@@ -176,7 +179,16 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
case graph.pipelinePass
|
||||
of CgenPass:
|
||||
if bModule != nil:
|
||||
finalCodegenActions(graph, BModule(bModule), finalNode)
|
||||
let disps = finalCodegenActions(graph, BModule(bModule), finalNode)
|
||||
if disps != nil:
|
||||
let ctx = preparePContext(graph, module, idgen)
|
||||
for disp in disps:
|
||||
let retTyp = disp.sym.typ[0]
|
||||
if retTyp != nil:
|
||||
# todo properly semcheck the code of dispatcher?
|
||||
createTypeBoundOps(graph, ctx, retTyp, disp.info, idgen)
|
||||
genProcAux(BModule(bModule), disp.sym)
|
||||
discard closePContext(graph, ctx, nil)
|
||||
of JSgenPass:
|
||||
when not defined(leanCompiler):
|
||||
discard finalJSCodeGen(graph, bModule, finalNode)
|
||||
@@ -215,7 +227,10 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
if result == nil:
|
||||
var cachedModules: seq[FileIndex]
|
||||
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
let filename = AbsoluteFile path
|
||||
if fileExists(filename): # it could be a stdinfile
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
result.flags.incl flags
|
||||
|
||||
@@ -31,7 +31,7 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
|
||||
return
|
||||
let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen)
|
||||
|
||||
let prc = newSym(skProc, n[3].ident, nextSymId c.idgen, iter.sym.owner, iter.sym.info)
|
||||
let prc = newSym(skProc, n[3].ident, c.idgen, iter.sym.owner, iter.sym.info)
|
||||
prc.typ = copyType(iter.sym.typ, nextTypeId c.idgen, prc)
|
||||
excl prc.typ.flags, tfCapturesEnv
|
||||
prc.typ.n.add newSymNode(getEnvParam(iter.sym))
|
||||
|
||||
@@ -26,7 +26,7 @@ proc semLocals*(c: PContext, n: PNode): PNode =
|
||||
{tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}:
|
||||
|
||||
if it.owner == owner:
|
||||
var field = newSym(skField, it.name, nextSymId c.idgen, owner, n.info)
|
||||
var field = newSym(skField, it.name, c.idgen, owner, n.info)
|
||||
field.typ = it.typ.skipTypes({tyVar})
|
||||
field.position = counter
|
||||
inc(counter)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import
|
||||
os, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
|
||||
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
|
||||
types, lookups, lineinfos, pathutils, linter
|
||||
types, lookups, lineinfos, pathutils, linter, modulepaths
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -34,7 +34,7 @@ const
|
||||
wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl,
|
||||
wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe,
|
||||
wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy,
|
||||
wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect}
|
||||
wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky}
|
||||
converterPragmas* = procPragmas
|
||||
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
|
||||
templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
|
||||
@@ -71,7 +71,8 @@ const
|
||||
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
|
||||
wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
|
||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||
wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||
wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl,
|
||||
wSendable}
|
||||
fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor,
|
||||
wRequiresInit, wNoalias, wAlign} - {wExportNims, wNodecl} # why exclude these?
|
||||
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
||||
@@ -83,7 +84,7 @@ const
|
||||
wGensym, wInject,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wDefine,
|
||||
wCompilerProc, wCore}
|
||||
paramPragmas* = {wNoalias, wInject, wGensym}
|
||||
paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl}
|
||||
letPragmas* = varPragmas
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
|
||||
wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe,
|
||||
@@ -116,7 +117,15 @@ const
|
||||
proc invalidPragma*(c: PContext; n: PNode) =
|
||||
localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments}))
|
||||
proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) =
|
||||
localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'")
|
||||
var msg = "cannot attach a custom pragma to '" & s.name.s & "'"
|
||||
if s != nil:
|
||||
msg.add("; custom pragmas are not supported for ")
|
||||
case s.kind
|
||||
of skForVar: msg.add("`for` loop variables")
|
||||
of skEnumField: msg.add("enum fields")
|
||||
of skModule: msg.add("modules")
|
||||
else: msg.add("symbol kind " & $s.kind)
|
||||
localError(c.config, n.info, msg)
|
||||
|
||||
proc pragmaProposition(c: PContext, n: PNode) =
|
||||
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||
@@ -131,7 +140,7 @@ proc pragmaEnsures(c: PContext, n: PNode) =
|
||||
openScope(c)
|
||||
let o = getCurrOwner(c)
|
||||
if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil:
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId(c.idgen), o, n.info)
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info)
|
||||
s.typ = o.typ.sons[0]
|
||||
incl(s.flags, sfUsed)
|
||||
addDecl(c, s)
|
||||
@@ -165,9 +174,7 @@ proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
|
||||
when hasFFI:
|
||||
s.cname = $s.loc.r
|
||||
if c.config.cmd == cmdNimfix and '$' notin extname:
|
||||
# note that '{.importc.}' is transformed into '{.importc: "$1".}'
|
||||
s.loc.flags.incl(lfFullExternalName)
|
||||
|
||||
|
||||
proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
setExternName(c, s, extname, info)
|
||||
@@ -202,9 +209,9 @@ proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
let m = s.getModule()
|
||||
incl(m.flags, sfCompileToObjc)
|
||||
|
||||
proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} =
|
||||
proc newEmptyStrNode(c: PContext; n: PNode, strVal: string = ""): PNode {.noinline.} =
|
||||
result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
|
||||
result.strVal = ""
|
||||
result.strVal = strVal
|
||||
|
||||
proc getStrLitNode(c: PContext, n: PNode): PNode =
|
||||
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||
@@ -236,8 +243,17 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
|
||||
if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
|
||||
else: result = defaultStr
|
||||
|
||||
proc processVirtual(c: PContext, n: PNode, s: PSym) =
|
||||
s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1"))
|
||||
s.constraint.strVal = s.constraint.strVal % s.name.s
|
||||
s.flags.incl {sfVirtual, sfInfixCall, sfExportc, sfMangleCpp}
|
||||
|
||||
s.typ.callConv = ccNoConvention
|
||||
incl c.config.globalOptions, optMixedMode
|
||||
|
||||
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
|
||||
sym.constraint = getStrLitNode(c, n)
|
||||
sym.flags.incl sfCodegenDecl
|
||||
|
||||
proc processMagic(c: PContext, n: PNode, s: PSym) =
|
||||
#if sfSystemModule notin c.module.flags:
|
||||
@@ -302,7 +318,7 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
|
||||
result.path = path
|
||||
c.libs.add result
|
||||
if path.kind in {nkStrLit..nkTripleStrLit}:
|
||||
result.isOverriden = options.isDynlibOverride(c.config, path.strVal)
|
||||
result.isOverridden = options.isDynlibOverride(c.config, path.strVal)
|
||||
|
||||
proc expectDynlibNode(c: PContext, n: PNode): PNode =
|
||||
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||
@@ -314,7 +330,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
|
||||
# {.dynlib: myGetProcAddr(...).}
|
||||
result = c.semExpr(c, n[1])
|
||||
if result.kind == nkSym and result.sym.kind == skConst:
|
||||
result = result.sym.astdef # look it up
|
||||
result = c.semConstExpr(c, result) # fold const
|
||||
if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
|
||||
localError(c.config, n.info, errStringLiteralExpected)
|
||||
result = newEmptyStrNode(c, n)
|
||||
@@ -322,12 +338,12 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
|
||||
proc processDynLib(c: PContext, n: PNode, sym: PSym) =
|
||||
if (sym == nil) or (sym.kind == skModule):
|
||||
let lib = getLib(c, libDynamic, expectDynlibNode(c, n))
|
||||
if not lib.isOverriden:
|
||||
if not lib.isOverridden:
|
||||
c.optionStack[^1].dynlib = lib
|
||||
else:
|
||||
if n.kind in nkPragmaCallKinds:
|
||||
var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
|
||||
if not lib.isOverriden:
|
||||
if not lib.isOverridden:
|
||||
addToLib(lib, sym)
|
||||
incl(sym.loc.flags, lfDynamicLib)
|
||||
else:
|
||||
@@ -390,6 +406,7 @@ proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} =
|
||||
of wImplicitStatic: {optImplicitStatic}
|
||||
of wPatterns, wTrMacros: {optTrMacros}
|
||||
of wSinkInference: {optSinkInference}
|
||||
of wQuirky: {optQuirky}
|
||||
else: {}
|
||||
|
||||
proc processExperimental(c: PContext; n: PNode) =
|
||||
@@ -451,6 +468,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
|
||||
# calling conventions (boring...):
|
||||
localError(c.config, n.info, "option expected")
|
||||
|
||||
proc checkPushedPragma(c: PContext, n: PNode) =
|
||||
let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1
|
||||
var key = if keyDeep: n[0] else: n
|
||||
if key.kind in nkIdentKinds:
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
var userPragma = strTableGet(c.userPragmas, ident)
|
||||
if userPragma == nil:
|
||||
let k = whichKeyword(ident)
|
||||
# TODO: might as well make a list which is not accepted by `push`: emit, cast etc.
|
||||
if k == wEmit:
|
||||
localError(c.config, n.info, "an 'emit' pragma cannot be pushed")
|
||||
|
||||
proc processPush(c: PContext, n: PNode, start: int) =
|
||||
if n[start-1].kind in nkPragmaCallKinds:
|
||||
localError(c.config, n.info, "'push' cannot have arguments")
|
||||
@@ -458,6 +487,7 @@ proc processPush(c: PContext, n: PNode, start: int) =
|
||||
for i in start..<n.len:
|
||||
if not tryProcessOption(c, n[i], c.config.options):
|
||||
# simply store it somewhere:
|
||||
checkPushedPragma(c, n[i])
|
||||
if x.otherPragmas.isNil:
|
||||
x.otherPragmas = newNodeI(nkPragma, n.info)
|
||||
x.otherPragmas.add n[i]
|
||||
@@ -509,6 +539,10 @@ proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile =
|
||||
if result.isEmpty: result = AbsoluteFile s
|
||||
|
||||
proc processCompile(c: PContext, n: PNode) =
|
||||
## This pragma can take two forms. The first is a simple file input:
|
||||
## {.compile: "file.c".}
|
||||
## The second is a tuple where the second arg is the output name strutils formatter:
|
||||
## {.compile: ("file.c", "$1.o").}
|
||||
proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) =
|
||||
var cf = Cfile(nimname: splitFile(src).name,
|
||||
cname: src, obj: dest, flags: {CfileFlag.External},
|
||||
@@ -523,7 +557,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
n[i] = c.semConstExpr(c, n[i])
|
||||
case n[i].kind
|
||||
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result = n[i].strVal
|
||||
else:
|
||||
shallowCopy(result, n[i].strVal)
|
||||
@@ -557,7 +591,8 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
else:
|
||||
found = findFile(c.config, s)
|
||||
if found.isEmpty: found = AbsoluteFile s
|
||||
let obj = toObjFile(c.config, completeCfilePath(c.config, found, false))
|
||||
let mangled = completeCfilePath(c.config, mangleModuleName(c.config, found).AbsoluteFile)
|
||||
let obj = toObjFile(c.config, mangled)
|
||||
docompile(c, it, found, obj, customArgs)
|
||||
|
||||
proc processLink(c: PContext, n: PNode) =
|
||||
@@ -609,7 +644,7 @@ proc pragmaEmit(c: PContext, n: PNode) =
|
||||
if n1.kind == nkBracket:
|
||||
var b = newNodeI(nkBracket, n1.info, n1.len)
|
||||
for i in 0..<n1.len:
|
||||
b[i] = c.semExpr(c, n1[i])
|
||||
b[i] = c.semExprWithType(c, n1[i], {efTypeAllowed})
|
||||
n[1] = b
|
||||
else:
|
||||
n[1] = c.semConstExpr(c, n1)
|
||||
@@ -662,7 +697,8 @@ proc processPragma(c: PContext, n: PNode, i: int) =
|
||||
elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
|
||||
invalidPragma(c, n)
|
||||
|
||||
var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), c.module, it.info, c.config.options)
|
||||
var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options)
|
||||
styleCheckDef(c, userPragma)
|
||||
userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
|
||||
strTableAdd(c.userPragmas, userPragma)
|
||||
|
||||
@@ -722,19 +758,8 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
|
||||
return
|
||||
if pragma.kind != nkBracket:
|
||||
localError(c.config, pragma.info, "list of key:value pairs expected"); return
|
||||
for n in pragma:
|
||||
if n.kind in nkPragmaCallKinds and n.len == 2:
|
||||
let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
|
||||
if dest == nil or dest.kind in routineKinds:
|
||||
localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
|
||||
let src = considerQuotedIdent(c, n[0])
|
||||
let alias = newSym(skAlias, src, nextSymId(c.idgen), dest, n[0].info, c.config.options)
|
||||
incl(alias.flags, sfExported)
|
||||
if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
|
||||
addInterfaceDecl(c, alias)
|
||||
n[1] = newSymNode(dest)
|
||||
else:
|
||||
localError(c.config, n.info, "key:value pair expected")
|
||||
message(c.config, pragma.info, warnDeprecated,
|
||||
"deprecated statement is now a no-op, use regular deprecated pragma")
|
||||
|
||||
proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||
if it.kind notin nkPragmaCallKinds or it.len != 2:
|
||||
@@ -750,12 +775,12 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
|
||||
# We return a dummy symbol; later passes over the type will repair it.
|
||||
# Generic instantiation needs to know about this too. But we're lazy
|
||||
# and perform the lookup on demand instead.
|
||||
result = newSym(skUnknown, considerQuotedIdent(c, n), nextSymId(c.idgen), nil, n.info,
|
||||
result = newSym(skUnknown, considerQuotedIdent(c, n), c.idgen, nil, n.info,
|
||||
c.config.options)
|
||||
else:
|
||||
result = qualifiedLookUp(c, n, {checkUndeclared})
|
||||
|
||||
proc semCustomPragma(c: PContext, n: PNode): PNode =
|
||||
proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
|
||||
var callNode: PNode
|
||||
|
||||
if n.kind in {nkIdent, nkSym}:
|
||||
@@ -775,6 +800,11 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
|
||||
invalidPragma(c, n)
|
||||
return n
|
||||
|
||||
# we have a valid custom pragma
|
||||
if sym != nil and sym.kind in {skEnumField, skForVar, skModule}:
|
||||
illegalCustomPragma(c, n, sym)
|
||||
return n
|
||||
|
||||
result = r
|
||||
# Transform the nkCall node back to its original form if possible
|
||||
if n.kind == nkIdent and r.len == 1:
|
||||
@@ -808,7 +838,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
validPragmas: TSpecialWords,
|
||||
comesFromPush, isStatement: bool): bool =
|
||||
var it = n[i]
|
||||
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it[0] else: it
|
||||
let keyDeep = it.kind in nkPragmaCallKinds and it.len > 1
|
||||
var key = if keyDeep: it[0] else: it
|
||||
if key.kind == nkBracketExpr:
|
||||
processNote(c, it)
|
||||
return
|
||||
@@ -822,7 +853,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else: discard
|
||||
return
|
||||
elif key.kind notin nkIdentKinds:
|
||||
n[i] = semCustomPragma(c, it)
|
||||
n[i] = semCustomPragma(c, it, sym)
|
||||
return
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
var userPragma = strTableGet(c.userPragmas, ident)
|
||||
@@ -834,6 +865,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if c.instCounter > 100:
|
||||
globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
|
||||
|
||||
if keyDeep:
|
||||
localError(c.config, it.info, "user pragma cannot have arguments")
|
||||
|
||||
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
|
||||
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
|
||||
@@ -841,7 +875,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else:
|
||||
let k = whichKeyword(ident)
|
||||
if k in validPragmas:
|
||||
checkPragmaUse(c.config, key.info, k, ident.s)
|
||||
checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module))
|
||||
case k
|
||||
of wExportc, wExportCpp:
|
||||
makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)
|
||||
@@ -950,8 +984,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
# only supported for backwards compat, doesn't do anything anymore
|
||||
noVal(c, it)
|
||||
of wConstructor:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, 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.typ.callConv = ccNoConvention
|
||||
of wHeader:
|
||||
var lib = getLib(c, libHeader, getStrLitNode(c, it))
|
||||
addToLib(lib, sym)
|
||||
@@ -1045,6 +1083,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if sym.typ != nil:
|
||||
incl(sym.typ.flags, 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)
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
of wGcSafe:
|
||||
noVal(c, it)
|
||||
if sym != nil:
|
||||
@@ -1081,13 +1125,20 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
of wFatal: fatal(c.config, it.info, expectStrLit(c, it))
|
||||
of wDefine: processDefine(c, it, sym)
|
||||
of wUndef: processUndef(c, it)
|
||||
of wCompile: processCompile(c, it)
|
||||
of wCompile:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, sfUsed)
|
||||
processCompile(c, it)
|
||||
of wLink: processLink(c, it)
|
||||
of wPassl:
|
||||
let m = sym.getModule()
|
||||
incl(m.flags, 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)
|
||||
let s = expectStrLit(c, it)
|
||||
extccomp.addCompileOption(c.config, s)
|
||||
recordPragma(c, it, "passc", s)
|
||||
@@ -1169,13 +1220,17 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
invalidPragma(c, it)
|
||||
of wByRef:
|
||||
noVal(c, it)
|
||||
if sym == nil or sym.typ == nil:
|
||||
if sym != nil and sym.kind == skParam:
|
||||
sym.options.incl optByRef
|
||||
elif sym == nil or sym.typ == nil:
|
||||
processOption(c, it, c.config.options)
|
||||
else:
|
||||
incl(sym.typ.flags, tfByRef)
|
||||
of wByCopy:
|
||||
noVal(c, it)
|
||||
if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
|
||||
if sym.kind == skParam:
|
||||
incl(sym.flags, sfByCopy)
|
||||
elif sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
|
||||
else: incl(sym.typ.flags, tfByCopy)
|
||||
of wPartial:
|
||||
noVal(c, it)
|
||||
@@ -1240,21 +1295,19 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
pragmaProposition(c, it)
|
||||
of wEnsures:
|
||||
pragmaEnsures(c, it)
|
||||
of wEnforceNoRaises:
|
||||
of wEnforceNoRaises, wQuirky:
|
||||
sym.flags.incl sfNeverRaises
|
||||
of wSystemRaisesDefect:
|
||||
sym.flags.incl sfSystemRaisesDefect
|
||||
of wVirtual:
|
||||
processVirtual(c, it, sym)
|
||||
|
||||
else: invalidPragma(c, it)
|
||||
elif comesFromPush and whichKeyword(ident) != wInvalid:
|
||||
discard "ignore the .push pragma; it doesn't apply"
|
||||
else:
|
||||
if sym == nil or (sym.kind in {skVar, skLet, skConst, skParam, skIterator,
|
||||
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
||||
n[i] = semCustomPragma(c, it)
|
||||
elif sym != nil:
|
||||
illegalCustomPragma(c, it, sym)
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
# semCustomPragma gives appropriate error for invalid pragmas
|
||||
n[i] = semCustomPragma(c, it, sym)
|
||||
|
||||
proc overwriteLineInfo(n: PNode; info: TLineInfo) =
|
||||
n.info = info
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
- Note that this code has been translated from a bootstrapping version written in Pascal.
|
||||
- So the code is **not** a poster child of good Nim code.
|
||||
|
||||
See [Internals of the Nim Compiler](https://nim-lang.org/docs/intern.html) for more information.
|
||||
See [Internals of the Nim Compiler](https://nim-lang.github.io/Nim/intern.html) for more information.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{.used.}
|
||||
|
||||
import
|
||||
lexer, options, idents, strutils, ast, msgs, lineinfos
|
||||
lexer, options, idents, strutils, ast, msgs, lineinfos, wordrecg
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, formatfloat]
|
||||
@@ -31,6 +31,10 @@ type
|
||||
length*: int16
|
||||
sym*: PSym
|
||||
|
||||
Section = enum
|
||||
GenericParams
|
||||
ObjectDef
|
||||
|
||||
TRenderTokSeq* = seq[TRenderTok]
|
||||
TSrcGen* = object
|
||||
indent*: int
|
||||
@@ -45,7 +49,7 @@ type
|
||||
pendingWhitespace: int
|
||||
comStack*: seq[PNode] # comment stack
|
||||
flags*: TRenderFlags
|
||||
inGenericParams: bool
|
||||
inside: set[Section] # Keeps track of contexts we are in
|
||||
checkAnon: bool # we're in a context that can contain sfAnon
|
||||
inPragma: int
|
||||
when defined(nimpretty):
|
||||
@@ -73,6 +77,16 @@ proc isKeyword*(i: PIdent): bool =
|
||||
(i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
|
||||
result = true
|
||||
|
||||
proc isExported(n: PNode): bool =
|
||||
## Checks if an ident is exported.
|
||||
## This is meant to be used with idents in nkIdentDefs.
|
||||
case n.kind
|
||||
of nkPostfix:
|
||||
n[0].ident.s == "*" and n[1].kind == nkIdent
|
||||
of nkPragmaExpr:
|
||||
n[0].isExported()
|
||||
else: false
|
||||
|
||||
proc renderDefinitionName*(s: PSym, noQuotes = false): string =
|
||||
## Returns the definition name of the symbol.
|
||||
##
|
||||
@@ -85,6 +99,25 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
|
||||
else:
|
||||
result = '`' & x & '`'
|
||||
|
||||
template inside(g: var TSrcGen, section: Section, body: untyped) =
|
||||
## Runs `body` with `section` included in `g.inside`.
|
||||
## Removes it at the end of the body if `g` wasn't inside it
|
||||
## before the template.
|
||||
let wasntInSection = section notin g.inside
|
||||
g.inside.incl section
|
||||
body
|
||||
if wasntInSection:
|
||||
g.inside.excl section
|
||||
|
||||
template outside(g: var TSrcGen, section: Section, body: untyped) =
|
||||
## Temporarily removes `section` from `g.inside`. Adds it back
|
||||
## at the end of the body if `g` was inside it before the template
|
||||
let wasInSection = section in g.inside
|
||||
g.inside.excl section
|
||||
body
|
||||
if wasInSection:
|
||||
g.inside.incl section
|
||||
|
||||
const
|
||||
IndentWidth = 2
|
||||
longIndentWid = IndentWidth * 2
|
||||
@@ -121,7 +154,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
|
||||
g.flags = renderFlags
|
||||
g.pendingNL = -1
|
||||
g.pendingWhitespace = -1
|
||||
g.inGenericParams = false
|
||||
g.inside = {}
|
||||
g.config = config
|
||||
|
||||
proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
||||
@@ -379,6 +412,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 nkStrLit: result = ""; result.addQuoted(n.strVal)
|
||||
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
|
||||
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
|
||||
@@ -814,7 +848,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
|
||||
gsub(g, n[^1], c)
|
||||
|
||||
proc genSymSuffix(result: var string, s: PSym) {.inline.} =
|
||||
if sfGenSym in s.flags:
|
||||
if sfGenSym in s.flags and s.name.id != ord(wUnderscore):
|
||||
result.add '_'
|
||||
result.addInt s.id
|
||||
|
||||
@@ -830,19 +864,17 @@ proc gproc(g: var TSrcGen, n: PNode) =
|
||||
|
||||
if n[patternPos].kind != nkEmpty:
|
||||
gpattern(g, n[patternPos])
|
||||
let oldInGenericParams = g.inGenericParams
|
||||
g.inGenericParams = true
|
||||
if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
|
||||
n[miscPos][1].kind != nkEmpty:
|
||||
gsub(g, n[miscPos][1])
|
||||
else:
|
||||
gsub(g, n[genericParamsPos])
|
||||
g.inGenericParams = oldInGenericParams
|
||||
g.inside(GenericParams):
|
||||
if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
|
||||
n[miscPos][1].kind != nkEmpty:
|
||||
gsub(g, n[miscPos][1])
|
||||
else:
|
||||
gsub(g, n[genericParamsPos])
|
||||
gsub(g, n[paramsPos])
|
||||
if renderNoPragmas notin g.flags:
|
||||
gsub(g, n[pragmasPos])
|
||||
if renderNoBody notin g.flags:
|
||||
if n[bodyPos].kind != nkEmpty:
|
||||
if n.len > bodyPos and n[bodyPos].kind != nkEmpty:
|
||||
put(g, tkSpaces, Space)
|
||||
putWithSpace(g, tkEquals, "=")
|
||||
indentNL(g)
|
||||
@@ -915,7 +947,7 @@ proc gasm(g: var TSrcGen, n: PNode) =
|
||||
gsub(g, n[1])
|
||||
|
||||
proc gident(g: var TSrcGen, n: PNode) =
|
||||
if g.inGenericParams and n.kind == nkSym:
|
||||
if GenericParams in g.inside and n.kind == nkSym:
|
||||
if sfAnon in n.sym.flags or
|
||||
(n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
|
||||
|
||||
@@ -939,7 +971,9 @@ proc gident(g: var TSrcGen, n: PNode) =
|
||||
s.addInt localId
|
||||
if sfCursor in n.sym.flags:
|
||||
s.add "_cursor"
|
||||
elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp):
|
||||
elif n.kind == nkSym and (renderIds in g.flags or
|
||||
(sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or
|
||||
n.sym.kind == skTemp):
|
||||
s.add '_'
|
||||
s.addInt n.sym.id
|
||||
when defined(debugMagics):
|
||||
@@ -1006,7 +1040,7 @@ proc accentedName(g: var TSrcGen, n: PNode) =
|
||||
gsub(g, n)
|
||||
|
||||
proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
|
||||
if i < 1 and i > 2: return
|
||||
if i < 1 or i > 2: return
|
||||
var needsParenthesis = false
|
||||
let nNext = n[i].skipHiddenNodes
|
||||
if nNext.kind == nkInfix:
|
||||
@@ -1303,14 +1337,31 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
gsub(g, n, pragmasPos)
|
||||
put(g, tkColon, ":")
|
||||
gsub(g, n, bodyPos)
|
||||
of nkConstDef, nkIdentDefs:
|
||||
of nkIdentDefs:
|
||||
# Skip if this is a property in a type and its not exported
|
||||
# (While also not allowing rendering of non exported fields)
|
||||
if ObjectDef in g.inside and (not n[0].isExported() and renderNonExportedFields notin g.flags):
|
||||
return
|
||||
# We render the identDef without being inside the section incase we render something like
|
||||
# y: proc (x: string) # (We wouldn't want to check if x is exported)
|
||||
g.outside(ObjectDef):
|
||||
gcomma(g, n, 0, -3)
|
||||
if n.len >= 2 and n[^2].kind != nkEmpty:
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n[^2], c)
|
||||
elif n.referencesUsing and renderExpandUsing in g.flags:
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, newSymNode(n.origUsingType), c)
|
||||
|
||||
if n.len >= 1 and n[^1].kind != nkEmpty:
|
||||
put(g, tkSpaces, Space)
|
||||
putWithSpace(g, tkEquals, "=")
|
||||
gsub(g, n[^1], c)
|
||||
of nkConstDef:
|
||||
gcomma(g, n, 0, -3)
|
||||
if n.len >= 2 and n[^2].kind != nkEmpty:
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n[^2], c)
|
||||
elif n.referencesUsing and renderExpandUsing in g.flags:
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, newSymNode(n.origUsingType), c)
|
||||
|
||||
if n.len >= 1 and n[^1].kind != nkEmpty:
|
||||
put(g, tkSpaces, Space)
|
||||
@@ -1333,6 +1384,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
putWithSpace(g, tkColon, ":")
|
||||
gsub(g, n, 1)
|
||||
of nkInfix:
|
||||
if n.len < 3:
|
||||
var i = 0
|
||||
put(g, tkOpr, "Too few children for nkInfix")
|
||||
return
|
||||
let oldLineLen = g.lineLen # we cache this because lineLen gets updated below
|
||||
infixArgument(g, n, 1)
|
||||
put(g, tkSpaces, Space)
|
||||
@@ -1380,15 +1435,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkAccQuoted:
|
||||
put(g, tkAccent, "`")
|
||||
for i in 0..<n.len:
|
||||
proc getStrVal(n: PNode): string =
|
||||
# pending https://github.com/nim-lang/Nim/pull/17540, use `getStrVal`
|
||||
case n.kind
|
||||
of nkIdent: n.ident.s
|
||||
of nkSym: n.sym.name.s
|
||||
else: ""
|
||||
proc isAlpha(n: PNode): bool =
|
||||
if n.kind in {nkIdent, nkSym}:
|
||||
let tmp = n.getStrVal
|
||||
let tmp = n.getPIdent.s
|
||||
result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'}
|
||||
var useSpace = false
|
||||
if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]:
|
||||
@@ -1473,22 +1522,20 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkObjectTy:
|
||||
if n.len > 0:
|
||||
putWithSpace(g, tkObject, "object")
|
||||
gsub(g, n[0])
|
||||
gsub(g, n[1])
|
||||
gcoms(g)
|
||||
gsub(g, n[2])
|
||||
g.inside(ObjectDef):
|
||||
gsub(g, n[0])
|
||||
gsub(g, n[1])
|
||||
gcoms(g)
|
||||
indentNL(g)
|
||||
gsub(g, n[2])
|
||||
dedent(g)
|
||||
else:
|
||||
put(g, tkObject, "object")
|
||||
of nkRecList:
|
||||
indentNL(g)
|
||||
for i in 0..<n.len:
|
||||
if n[i].kind == nkIdentDefs and n[i][0].kind == nkPostfix or
|
||||
renderNonExportedFields in g.flags:
|
||||
optNL(g)
|
||||
gsub(g, n[i], c)
|
||||
gcoms(g)
|
||||
dedent(g)
|
||||
putNL(g)
|
||||
optNL(g)
|
||||
gsub(g, n[i], c)
|
||||
gcoms(g)
|
||||
of nkOfInherit:
|
||||
putWithSpace(g, tkOf, "of")
|
||||
gsub(g, n, 0)
|
||||
|
||||
@@ -21,8 +21,8 @@ type
|
||||
# though it is not necessary)
|
||||
Rope* = string
|
||||
|
||||
proc newRopeAppender*(): string {.inline.} =
|
||||
result = newString(0)
|
||||
proc newRopeAppender*(cap = 80): string {.inline.} =
|
||||
result = newStringOfCap(cap)
|
||||
|
||||
proc freeze*(r: Rope) {.inline.} = discard
|
||||
|
||||
@@ -102,12 +102,9 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
|
||||
inc(i)
|
||||
else:
|
||||
doAssert false, "invalid format string: " & frmt
|
||||
var start = i
|
||||
while i < frmt.len:
|
||||
if frmt[i] != '$': inc(i)
|
||||
else: break
|
||||
if i - 1 >= start:
|
||||
result.add(substr(frmt, start, i - 1))
|
||||
else:
|
||||
result.add(frmt[i])
|
||||
inc(i)
|
||||
|
||||
proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
|
||||
runtimeFormat(frmt, args)
|
||||
|
||||
@@ -17,7 +17,7 @@ import
|
||||
pathutils, pipelines
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
import std/[syncio, assertions]
|
||||
|
||||
# we support 'cmpIgnoreStyle' natively for efficiency:
|
||||
from strutils import cmpIgnoreStyle, contains
|
||||
@@ -207,8 +207,8 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
|
||||
let oldGlobalOptions = conf.globalOptions
|
||||
let oldSelectedGC = conf.selectedGC
|
||||
undefSymbol(conf.symbols, "nimv2")
|
||||
conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors}
|
||||
unregisterArcOrc(conf)
|
||||
conf.globalOptions.excl optOwnedRefs
|
||||
conf.selectedGC = gcUnselected
|
||||
|
||||
var m = graph.makeModule(scriptName)
|
||||
@@ -227,9 +227,20 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
if optOwnedRefs in oldGlobalOptions:
|
||||
conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
|
||||
defineSymbol(conf.symbols, "nimv2")
|
||||
if conf.selectedGC in {gcArc, gcOrc}:
|
||||
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
|
||||
defineSymbol(conf.symbols, "nimv2")
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
case conf.selectedGC
|
||||
of gcArc:
|
||||
defineSymbol(conf.symbols, "gcarc")
|
||||
of gcOrc:
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
of gcAtomicArc:
|
||||
defineSymbol(conf.symbols, "gcatomicarc")
|
||||
else:
|
||||
doAssert false, "unreachable"
|
||||
|
||||
# ensure we load 'system.nim' again for the real non-config stuff!
|
||||
resetSystemArtifacts(graph)
|
||||
|
||||
169
compiler/sem.nim
169
compiler/sem.nim
@@ -9,6 +9,8 @@
|
||||
|
||||
# This module implements the semantic checking pass.
|
||||
|
||||
import tables
|
||||
|
||||
import
|
||||
ast, strutils, options, astalgo, trees,
|
||||
wordrecg, ropes, msgs, idents, renderer, types, platform, math,
|
||||
@@ -19,14 +21,15 @@ import
|
||||
lowerings, plugins/active, lineinfos, strtabs, int128,
|
||||
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs
|
||||
|
||||
when defined(nimfix):
|
||||
import nimfix/prettybase
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import spawn
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/formatfloat
|
||||
import std/[
|
||||
formatfloat,
|
||||
assertions,
|
||||
]
|
||||
|
||||
# implementation
|
||||
|
||||
@@ -205,13 +208,78 @@ proc commonType*(c: PContext; x, y: PType): PType =
|
||||
result = newType(k, nextTypeId(c.idgen), r.owner)
|
||||
result.addSonSkipIntLit(r, c.idgen)
|
||||
|
||||
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
|
||||
proc shouldCheckCaseCovered(caseTyp: PType): bool =
|
||||
result = false
|
||||
case caseTyp.kind
|
||||
of shouldChckCovered:
|
||||
result = true
|
||||
of tyRange:
|
||||
if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
|
||||
result = true
|
||||
else:
|
||||
discard
|
||||
|
||||
proc endsInNoReturn(n: PNode): bool =
|
||||
# check if expr ends in raise exception or call of noreturn proc
|
||||
## check if expr ends the block like raising or call of noreturn procs do
|
||||
result = false # assume it does return
|
||||
|
||||
template checkBranch(branch) =
|
||||
if not endsInNoReturn(branch):
|
||||
# proved a branch returns
|
||||
return false
|
||||
|
||||
var it = n
|
||||
# skip these beforehand, no special handling needed
|
||||
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
|
||||
it = it.lastSon
|
||||
result = it.kind in nkLastBlockStmts or
|
||||
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
|
||||
case it.kind
|
||||
of nkIfStmt:
|
||||
var hasElse = false
|
||||
for branch in it:
|
||||
checkBranch:
|
||||
if branch.len == 2:
|
||||
branch[1]
|
||||
elif branch.len == 1:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `if` statement during endsInNoReturn"
|
||||
# none of the branches returned
|
||||
result = hasElse # Only truly a no-return when it's exhaustive
|
||||
of nkCaseStmt:
|
||||
let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
|
||||
# semCase should already have checked for exhaustiveness in this case
|
||||
# effectively the same as having an else
|
||||
var hasElse = caseTyp.shouldCheckCaseCovered()
|
||||
|
||||
# actual noreturn checks
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch:
|
||||
case branch.kind
|
||||
of nkOfBranch:
|
||||
branch[^1]
|
||||
of nkElifBranch:
|
||||
branch[1]
|
||||
of nkElse:
|
||||
hasElse = true
|
||||
branch[0]
|
||||
else:
|
||||
raiseAssert "Malformed `case` statement in endsInNoReturn"
|
||||
# Can only guarantee a noreturn if there is an else or it's exhaustive
|
||||
result = hasElse
|
||||
of nkTryStmt:
|
||||
checkBranch(it[0])
|
||||
for i in 1 ..< it.len:
|
||||
let branch = it[i]
|
||||
checkBranch(branch[^1])
|
||||
# none of the branches returned
|
||||
result = true
|
||||
else:
|
||||
result = it.kind in nkLastBlockStmts or
|
||||
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||
|
||||
proc commonType*(c: PContext; x: PType, y: PNode): PType =
|
||||
# ignore exception raising branches in case/if expressions
|
||||
@@ -219,7 +287,7 @@ proc commonType*(c: PContext; x: PType, y: PNode): PType =
|
||||
commonType(c, x, y.typ)
|
||||
|
||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
when defined(nimsuggest):
|
||||
suggestDecl(c, n, result)
|
||||
|
||||
@@ -242,7 +310,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||
# template; we must fix it here: see #909
|
||||
result.owner = getCurrOwner(c)
|
||||
else:
|
||||
result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
|
||||
result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
|
||||
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
|
||||
# incl(result.flags, sfGlobal)
|
||||
when defined(nimsuggest):
|
||||
@@ -281,7 +349,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||
|
||||
proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
|
||||
if t.sym != nil: return t.sym
|
||||
result = newSym(skType, getIdent(c.cache, "AnonType"), nextSymId c.idgen, t.owner, info)
|
||||
result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info)
|
||||
result.flags.incl sfAnon
|
||||
result.typ = t
|
||||
|
||||
@@ -351,6 +419,11 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
c.config.m.errorOutputs = {}
|
||||
c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
|
||||
|
||||
when defined(nimsuggest):
|
||||
# Remove the error hook so nimsuggest doesn't report errors there
|
||||
let tempHook = c.graph.config.structuredErrorHook
|
||||
c.graph.config.structuredErrorHook = nil
|
||||
|
||||
try:
|
||||
result = evalConstExpr(c.module, c.idgen, c.graph, e)
|
||||
if result == nil or result.kind == nkEmpty:
|
||||
@@ -361,6 +434,10 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
except ERecoverableError:
|
||||
result = nil
|
||||
|
||||
when defined(nimsuggest):
|
||||
# Restore the error hook
|
||||
c.graph.config.structuredErrorHook = tempHook
|
||||
|
||||
c.config.errorCounter = oldErrorCount
|
||||
c.config.errorMax = oldErrorMax
|
||||
c.config.m.errorOutputs = oldErrorOutputs
|
||||
@@ -405,6 +482,8 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedTyp
|
||||
evaluated = evalAtCompileTime(c, result)
|
||||
if evaluated != nil: return evaluated
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode
|
||||
|
||||
include hlo, seminst, semcall
|
||||
|
||||
proc resetSemFlag(n: PNode) =
|
||||
@@ -460,13 +539,22 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
|
||||
# we now know the supplied arguments
|
||||
var paramTypes = newIdTable()
|
||||
for param, value in genericParamsInMacroCall(s, call):
|
||||
idTablePut(paramTypes, param.typ, value.typ)
|
||||
var givenType = value.typ
|
||||
# the sym nodes used for the supplied generic arguments for
|
||||
# templates and macros leave type nil so regular sem can handle it
|
||||
# in this case, get the type directly from the sym
|
||||
if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
|
||||
givenType = value.sym.typ
|
||||
idTablePut(paramTypes, param.typ, givenType)
|
||||
|
||||
retType = generateTypeInstance(c, paramTypes,
|
||||
macroResult.info, retType)
|
||||
|
||||
result = semExpr(c, result, flags, expectedType)
|
||||
result = fitNode(c, retType, result, result.info)
|
||||
if retType.kind == tyVoid:
|
||||
result = semStmt(c, result, flags)
|
||||
else:
|
||||
result = semExpr(c, result, flags, expectedType)
|
||||
result = fitNode(c, retType, result, result.info)
|
||||
#globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0]))
|
||||
dec(c.config.evalTemplateCounter)
|
||||
discard c.friendModules.pop()
|
||||
@@ -510,8 +598,6 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
|
||||
result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool)))
|
||||
if result.kind != nkIntLit:
|
||||
localError(c.config, n.info, errConstExprExpected)
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode
|
||||
proc semConceptBody(c: PContext, n: PNode): PNode
|
||||
|
||||
include semtypes
|
||||
@@ -550,17 +636,17 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
|
||||
if endsWithElse:
|
||||
return caseExpr.len - 1
|
||||
|
||||
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode]
|
||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
|
||||
proc defaultNodeField(c: PContext, a: PNode): PNode
|
||||
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode]
|
||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode
|
||||
proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode
|
||||
|
||||
const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
|
||||
|
||||
proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] =
|
||||
proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] =
|
||||
case recNode.kind
|
||||
of nkRecList:
|
||||
for field in recNode:
|
||||
result.add defaultFieldsForTuple(c, field, hasDefault)
|
||||
result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault)
|
||||
of nkSym:
|
||||
let field = recNode.sym
|
||||
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
|
||||
@@ -569,30 +655,30 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
|
||||
result.add newTree(nkExprColonExpr, recNode, field.ast)
|
||||
else:
|
||||
if recType.kind in {tyObject, tyArray, tyTuple}:
|
||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
|
||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
|
||||
if asgnExpr != nil:
|
||||
hasDefault = true
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
return
|
||||
|
||||
let asgnType = newType(tyTypeDesc, nextTypeId(c.idgen), recType.owner)
|
||||
rawAddSon(asgnType, recType)
|
||||
let asgnType = newType(tyTypeDesc, nextTypeId(c.idgen), recNode.typ.owner)
|
||||
rawAddSon(asgnType, recNode.typ)
|
||||
let asgnExpr = newTree(nkCall,
|
||||
newSymNode(getSysMagic(c.graph, recNode.info, "zeroDefault", mZeroDefault)),
|
||||
newNodeIT(nkType, recNode.info, asgnType)
|
||||
)
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
asgnExpr.typ = recType
|
||||
asgnExpr.typ = recNode.typ
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
else:
|
||||
doAssert false
|
||||
|
||||
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
|
||||
proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] =
|
||||
case recNode.kind
|
||||
of nkRecList:
|
||||
for field in recNode:
|
||||
result.add defaultFieldsForTheUninitialized(c, field)
|
||||
result.add defaultFieldsForTheUninitialized(c, field, checkDefault)
|
||||
of nkRecCase:
|
||||
let discriminator = recNode[0]
|
||||
var selectedBranch: int
|
||||
@@ -601,58 +687,60 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
|
||||
# None of the branches were explicitly selected by the user and no value
|
||||
# was given to the discrimator. We can assume that it will be initialized
|
||||
# to zero and this will select a particular branch as a result:
|
||||
if checkDefault: # don't add defaults when checking whether a case branch has default fields
|
||||
return
|
||||
defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
|
||||
defaultValue.typ = discriminator.typ
|
||||
selectedBranch = recNode.pickCaseBranchIndex defaultValue
|
||||
defaultValue.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
|
||||
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
|
||||
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault)
|
||||
of nkSym:
|
||||
let field = recNode.sym
|
||||
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
|
||||
if field.ast != nil: #Try to use default value
|
||||
result.add newTree(nkExprColonExpr, recNode, field.ast)
|
||||
elif recType.kind in {tyObject, tyArray, tyTuple}:
|
||||
let asgnExpr = defaultNodeField(c, recNode, recType)
|
||||
let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
|
||||
if asgnExpr != nil:
|
||||
asgnExpr.typ = recType
|
||||
asgnExpr.typ = recNode.typ
|
||||
asgnExpr.flags.incl nfSkipFieldChecking
|
||||
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
|
||||
else:
|
||||
doAssert false
|
||||
|
||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
|
||||
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
|
||||
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
|
||||
if aTypSkip.kind == tyObject:
|
||||
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n)
|
||||
let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
|
||||
if child.len > 0:
|
||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTypSkip))
|
||||
asgnExpr.typ = aTypSkip
|
||||
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
|
||||
asgnExpr.typ = aTyp
|
||||
asgnExpr.sons.add child
|
||||
result = semExpr(c, asgnExpr)
|
||||
elif aTypSkip.kind == tyArray:
|
||||
let child = defaultNodeField(c, a, aTypSkip[1])
|
||||
let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
|
||||
|
||||
if child != nil:
|
||||
let node = newNode(nkIntLit)
|
||||
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
|
||||
result = semExpr(c, newTree(nkCall, newSymNode(getCompilerProc(c.graph, "nimArrayWith"), a.info),
|
||||
semExprWithType(c, child),
|
||||
node
|
||||
))
|
||||
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
|
||||
semExprWithType(c, child),
|
||||
node
|
||||
))
|
||||
result.typ = aTyp
|
||||
elif aTypSkip.kind == tyTuple:
|
||||
var hasDefault = false
|
||||
if aTypSkip.n != nil:
|
||||
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault)
|
||||
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
|
||||
if hasDefault and children.len > 0:
|
||||
result = newNodeI(nkTupleConstr, a.info)
|
||||
result.typ = aTyp
|
||||
result.sons.add children
|
||||
result = semExpr(c, result)
|
||||
|
||||
proc defaultNodeField(c: PContext, a: PNode): PNode =
|
||||
result = defaultNodeField(c, a, a.typ)
|
||||
proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode =
|
||||
result = defaultNodeField(c, a, a.typ, checkDefault)
|
||||
|
||||
include semtempl, semgnrc, semstmts, semexprs
|
||||
|
||||
@@ -675,6 +763,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
|
||||
if result.p != nil: internalError(graph.config, module.info, "sem.preparePContext")
|
||||
result.semConstExpr = semConstExpr
|
||||
result.semExpr = semExpr
|
||||
result.semExprWithType = semExprWithType
|
||||
result.semTryExpr = tryExpr
|
||||
result.semTryConstExpr = tryConstExpr
|
||||
result.computeRequiresInit = computeRequiresInit
|
||||
|
||||
@@ -132,7 +132,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||
# just in case, should be impossible though
|
||||
if syms.len == 0:
|
||||
break
|
||||
|
||||
|
||||
if nextSymIndex > high(syms):
|
||||
# we have reached the end
|
||||
break
|
||||
@@ -236,44 +236,89 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add("\n")
|
||||
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
|
||||
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
|
||||
if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures:
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add("\n unknown named parameter")
|
||||
else:
|
||||
candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
|
||||
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
|
||||
of kExtraArg: candidates.add("\n extra argument given")
|
||||
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
|
||||
of kTypeMismatch, kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add("\n required type for " & nameParam & ": ")
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
candidates.add "\n but expression '"
|
||||
if err.firstMismatch.kind == kVarNeeded:
|
||||
if n.len > 1:
|
||||
if verboseTypeMismatch notin c.config.legacyFeatures:
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add(" unknown named parameter")
|
||||
else:
|
||||
candidates.add(" unknown named parameter: " & $nArg[0])
|
||||
candidates.add "\n"
|
||||
of kAlreadyGiven:
|
||||
candidates.add(" named param already provided: " & $nArg[0])
|
||||
candidates.add "\n"
|
||||
of kPositionalAlreadyGiven:
|
||||
candidates.add(" positional param was already given as named param")
|
||||
candidates.add "\n"
|
||||
of kExtraArg:
|
||||
candidates.add(" extra argument given")
|
||||
candidates.add "\n"
|
||||
of kMissingParam:
|
||||
candidates.add(" missing parameter: " & nameParam)
|
||||
candidates.add "\n"
|
||||
of kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add " expression '"
|
||||
candidates.add renderNotLValue(nArg)
|
||||
candidates.add "' is immutable, not 'var'"
|
||||
else:
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
let got = nArg.typ
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
candidates.add "\n"
|
||||
of kTypeMismatch:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
doAssert wanted != nil
|
||||
if got != nil:
|
||||
if got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
let got = nArg.typ
|
||||
if got != nil and got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.add " expression '"
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
effectProblem(wanted, got, candidates, c)
|
||||
candidates.add "\n"
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
else:
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add("\n unknown named parameter")
|
||||
else:
|
||||
candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
|
||||
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
|
||||
of kExtraArg: candidates.add("\n extra argument given")
|
||||
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
|
||||
of kTypeMismatch, kVarNeeded:
|
||||
doAssert nArg != nil
|
||||
let wanted = err.firstMismatch.formal.typ
|
||||
doAssert err.firstMismatch.formal != nil
|
||||
candidates.add("\n required type for " & nameParam & ": ")
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, wanted)
|
||||
candidates.add "\n but expression '"
|
||||
if err.firstMismatch.kind == kVarNeeded:
|
||||
candidates.add renderNotLValue(nArg)
|
||||
candidates.add "' is immutable, not 'var'"
|
||||
else:
|
||||
candidates.add renderTree(nArg)
|
||||
candidates.add "' is of type: "
|
||||
let got = nArg.typ
|
||||
candidates.addTypeDeclVerboseMaybe(c.config, got)
|
||||
doAssert wanted != nil
|
||||
if got != nil:
|
||||
if got.kind == tyProc and wanted.kind == tyProc:
|
||||
# These are proc mismatches so,
|
||||
# add the extra explict detail of the mismatch
|
||||
candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
|
||||
effectProblem(wanted, got, candidates, c)
|
||||
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
candidates.add "\n"
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
candidates.add "\n"
|
||||
if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
|
||||
n.kind == nkCommand:
|
||||
maybeWrongSpace = true
|
||||
@@ -293,7 +338,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
const
|
||||
errTypeMismatch = "type mismatch: got <"
|
||||
errButExpected = "but expected one of:"
|
||||
errExpectedPosition = "Expected one of (first mismatch at position [#]):"
|
||||
errExpectedPosition = "Expected one of (first mismatch at [position]):"
|
||||
errUndeclaredField = "undeclared field: '$1'"
|
||||
errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
|
||||
errBadRoutine = "attempting to call routine: '$1'$2"
|
||||
@@ -595,7 +640,12 @@ proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
else:
|
||||
x.call.add c.graph.emptyNode
|
||||
of skType:
|
||||
x.call.add newSymNode(s, n.info)
|
||||
var tn = newSymNode(s, n.info)
|
||||
# this node will be used in template substitution,
|
||||
# pretend this is an untyped node and let regular sem handle the type
|
||||
# to prevent problems where a generic parameter is treated as a value
|
||||
tn.typ = nil
|
||||
x.call.add tn
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
@@ -627,7 +677,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
candidates)
|
||||
result = semResolvedCall(c, r, n, flags)
|
||||
else:
|
||||
if efExplain notin flags:
|
||||
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
|
||||
result = semGenericStmt(c, n)
|
||||
result.typ = makeTypeFromExpr(c, result.copyTree)
|
||||
elif efExplain notin flags:
|
||||
# repeat the overload resolution,
|
||||
# this time enabling all the diagnostic output (this should fail again)
|
||||
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
|
||||
@@ -662,14 +715,18 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||
onUse(info, s)
|
||||
result = newSymNode(newInst, info)
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
## sems generic params in subscript expression
|
||||
for i in 1..<n.len:
|
||||
let e = semExprWithType(c, n[i])
|
||||
if e.typ == nil:
|
||||
n[i].typ = errorType(c)
|
||||
else:
|
||||
n[i].typ = e.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
assert n.kind == nkBracketExpr
|
||||
setGenericParams(c, n)
|
||||
var s = s
|
||||
var a = n[0]
|
||||
if a.kind == nkSym:
|
||||
@@ -704,17 +761,25 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||
else:
|
||||
result = explicitGenericInstError(c, n)
|
||||
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
# for borrowing the sym in the symbol table is returned, else nil.
|
||||
# New approach: generate fn(x, y, z) where x, y, z have the proper types
|
||||
# and use the overloading resolution mechanism:
|
||||
const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
|
||||
|
||||
template getType(isDistinct: bool; t: PType):untyped =
|
||||
if isDistinct: t.baseOfDistinct(c.graph, c.idgen) else: t
|
||||
|
||||
result = default(tuple[s: PSym, state: TBorrowState])
|
||||
var call = newNodeI(nkCall, fn.info)
|
||||
var hasDistinct = false
|
||||
var isDistinct: bool
|
||||
var x: PType
|
||||
var t: PType
|
||||
call.add(newIdentNode(fn.name, fn.info))
|
||||
for i in 1..<fn.typ.n.len:
|
||||
let param = fn.typ.n[i]
|
||||
const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
|
||||
#[.
|
||||
# We only want the type not any modifiers such as `ptr`, `var`, `ref` ...
|
||||
# tyCompositeTypeClass is here for
|
||||
@@ -723,22 +788,31 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
proc `$`(f: Foo): string {.borrow.}
|
||||
# We want to skip the `Foo` to get `int`
|
||||
]#
|
||||
let t = skipTypes(param.typ, desiredTypes)
|
||||
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
|
||||
var x: PType
|
||||
t = skipTypes(param.typ, desiredTypes)
|
||||
isDistinct = t.kind == tyDistinct or param.typ.kind == tyDistinct
|
||||
if t.kind == tyGenericInvocation and t[0].lastSon.kind == tyDistinct:
|
||||
result.state = bsGeneric
|
||||
return
|
||||
if isDistinct: hasDistinct = true
|
||||
if param.typ.kind == tyVar:
|
||||
x = newTypeS(param.typ.kind, c)
|
||||
x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
|
||||
x.addSonSkipIntLit(getType(isDistinct, t), c.idgen)
|
||||
else:
|
||||
x = t.baseOfDistinct(c.graph, c.idgen)
|
||||
call.add(newNodeIT(nkEmpty, fn.info, x))
|
||||
x = getType(isDistinct, t)
|
||||
var s = copySym(param.sym, c.idgen)
|
||||
s.typ = x
|
||||
s.info = param.info
|
||||
call.add(newSymNode(s))
|
||||
if hasDistinct:
|
||||
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
|
||||
var resolved = semOverloadedCall(c, call, call, filter, {})
|
||||
if resolved != nil:
|
||||
result = resolved[0].sym
|
||||
if not compareTypes(result.typ[0], fn.typ[0], dcEqIgnoreDistinct):
|
||||
result = nil
|
||||
elif result.magic in {mArrPut, mArrGet}:
|
||||
result.s = resolved[0].sym
|
||||
result.state = bsMatch
|
||||
if not compareTypes(result.s.typ[0], fn.typ[0], dcEqIgnoreDistinct, {IgnoreFlags}):
|
||||
result.state = bsReturnNotMatch
|
||||
elif result.s.magic in {mArrPut, mArrGet}:
|
||||
# cannot borrow these magics for now
|
||||
result = nil
|
||||
result.state = bsNotSupported
|
||||
else:
|
||||
result.state = bsNoDistinct
|
||||
|
||||
@@ -126,6 +126,7 @@ type
|
||||
libs*: seq[PLib] # all libs used by this module
|
||||
semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas
|
||||
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
|
||||
semExprWithType*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
|
||||
semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
||||
semTryConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.}
|
||||
computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.}
|
||||
@@ -166,6 +167,10 @@ type
|
||||
lastTLineInfo*: TLineInfo
|
||||
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.
|
||||
TBorrowState* = enum
|
||||
bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
|
||||
|
||||
template config*(c: PContext): ConfigRef = c.graph.config
|
||||
|
||||
@@ -430,7 +435,7 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||
incl typedesc.flags, tfCheckedForDestructor
|
||||
internalAssert(c.config, typ != nil)
|
||||
typedesc.addSonSkipIntLit(typ, c.idgen)
|
||||
let sym = newSym(skType, c.cache.idAnon, nextSymId(c.idgen), getCurrOwner(c), info,
|
||||
let sym = newSym(skType, c.cache.idAnon, c.idgen, getCurrOwner(c), info,
|
||||
c.config.options).linkTo(typedesc)
|
||||
result = newSymNode(sym, info)
|
||||
|
||||
|
||||
@@ -54,10 +54,6 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
# same as 'semExprWithType' but doesn't check for proc vars
|
||||
result = semExpr(c, n, flags + {efOperand})
|
||||
if result.typ != nil:
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree)
|
||||
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
|
||||
elif {efWantStmt, efAllowStmt} * flags != {}:
|
||||
result.typ = newTypeS(tyVoid, c)
|
||||
@@ -85,7 +81,10 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
|
||||
|
||||
proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
|
||||
let first = n[0].sym
|
||||
if first.kind == skEnumField:
|
||||
var foundSym: PSym = nil
|
||||
if first.kind == skEnumField and
|
||||
not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
|
||||
foundSym == first:
|
||||
# choose the first resolved enum field, i.e. the latest in scope
|
||||
# to mirror behavior before overloadable enums
|
||||
if hintAmbiguousEnum in c.config.notes:
|
||||
@@ -109,7 +108,7 @@ proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
|
||||
result = n
|
||||
|
||||
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
result = semExprCheck(c, n, flags, expectedType)
|
||||
result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType)
|
||||
if result.typ == nil and efInTypeof in flags:
|
||||
result.typ = c.voidType
|
||||
elif (result.typ == nil or result.typ.kind == tyNone) and
|
||||
@@ -123,6 +122,18 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType
|
||||
elif result.typ.kind == tyError:
|
||||
# associates the type error to the current owner
|
||||
result.typ = errorType(c)
|
||||
elif efTypeAllowed in flags and result.typ.kind == tyProc and
|
||||
hasUnresolvedParams(result, {}):
|
||||
# mirrored with semOperand but only on efTypeAllowed
|
||||
let owner = result.typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % n.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % n.renderTree
|
||||
localError(c.config, n.info, err)
|
||||
result.typ = errorType(c)
|
||||
else:
|
||||
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
|
||||
|
||||
@@ -219,6 +230,9 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
result = convNotInRange
|
||||
else:
|
||||
# we use d, s here to speed up that operation a bit:
|
||||
if d.kind == tyFromExpr:
|
||||
result = convNotLegal
|
||||
return
|
||||
case cmpTypes(c, d, s)
|
||||
of isNone, isGeneric:
|
||||
if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct):
|
||||
@@ -242,7 +256,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
|
||||
if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
|
||||
return false
|
||||
let conf = c.config
|
||||
if conf.selectedGC in {gcArc, gcOrc}:
|
||||
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
let d = skipTypes(dst, abstractInst)
|
||||
let s = skipTypes(src, abstractInst)
|
||||
if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal:
|
||||
@@ -261,7 +275,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
|
||||
return false
|
||||
elif srcSize < 0:
|
||||
return false
|
||||
elif typeAllowed(dst, skParam, c) != nil:
|
||||
elif typeAllowed(dst, skParam, c, {taIsCastable}) != nil:
|
||||
return false
|
||||
elif dst.kind == tyProc and dst.callConv == ccClosure:
|
||||
return src.kind == tyProc and src.callConv == ccClosure
|
||||
@@ -269,11 +283,6 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
|
||||
result = (dstSize >= srcSize) or
|
||||
(skipTypes(dst, abstractInst).kind in IntegralTypes) or
|
||||
(skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
|
||||
if result and (dstSize > srcSize):
|
||||
var warnMsg = "target type is larger than source type"
|
||||
warnMsg.add("\n target type: '$1' ($2)" % [$dst, if dstSize == 1: "1 byte" else: $dstSize & " bytes"])
|
||||
warnMsg.add("\n source type: '$1' ($2)" % [$src, if srcSize == 1: "1 byte" else: $srcSize & " bytes"])
|
||||
message(conf, info, warnCastSizes, warnMsg)
|
||||
if result and src.kind == tyNil:
|
||||
return dst.size <= conf.target.ptrSize
|
||||
|
||||
@@ -294,7 +303,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool =
|
||||
let s = qualifiedLookUp(c, n, {})
|
||||
result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
|
||||
|
||||
proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||
if n.len != 2:
|
||||
localError(c.config, n.info, "a type conversion takes exactly one argument")
|
||||
return n
|
||||
@@ -341,7 +350,10 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
if n[1].kind == nkExprEqExpr and
|
||||
targetType.skipTypes(abstractPtrs).kind == tyObject:
|
||||
localError(c.config, n.info, "object construction uses ':', not '='")
|
||||
var op = semExprWithType(c, n[1])
|
||||
var op = semExprWithType(c, n[1], flags * {efDetermineType})
|
||||
if op.kind == nkClosedSymChoice and op.len > 0 and
|
||||
op[0].sym.kind == skEnumField: # resolves overloadedable enums
|
||||
op = ambiguousSymChoice(c, n, op)
|
||||
if targetType.kind != tyGenericParam and targetType.isMetaType:
|
||||
let final = inferWithMetatype(c, targetType, op, true)
|
||||
result.add final
|
||||
@@ -353,7 +365,9 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
# here or needs to be overwritten too then.
|
||||
result.add op
|
||||
|
||||
if targetType.kind == tyGenericParam:
|
||||
if targetType.kind == tyGenericParam or
|
||||
(op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0):
|
||||
# expression is compiled early in a generic body
|
||||
result.typ = makeTypeFromExpr(c, copyTree(result))
|
||||
return result
|
||||
|
||||
@@ -367,7 +381,8 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||
elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
|
||||
op = fitNode(c, targetType, op, result.info)
|
||||
of convNotNeedeed:
|
||||
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||
if efNoSem2Check notin flags:
|
||||
message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||
of convNotLegal:
|
||||
result = fitNode(c, result.typ, result[1], result.info)
|
||||
if result == nil:
|
||||
@@ -469,6 +484,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
res = t.kind == tyProc and
|
||||
t.callConv == ccClosure
|
||||
of "iterator":
|
||||
# holdover from when `is iterator` didn't work
|
||||
let t = skipTypes(t1, abstractRange)
|
||||
res = t.kind == tyProc and
|
||||
t.callConv == ccClosure and
|
||||
@@ -496,8 +512,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result.typ = n.typ
|
||||
|
||||
proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if n.len != 3:
|
||||
if n.len != 3 or n[2].kind == nkEmpty:
|
||||
localError(c.config, n.info, "'is' operator takes 2 arguments")
|
||||
return errorNode(c, n)
|
||||
|
||||
let boolType = getSysType(c.graph, n.info, tyBool)
|
||||
result = n
|
||||
@@ -597,7 +614,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
a.add m
|
||||
changeType(m, tup[i], check)
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if check and n.kind != nkUInt64Lit and not sameType(n.typ, newType):
|
||||
if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType):
|
||||
let value = n.intVal
|
||||
if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
|
||||
localError(c.config, n.info, "cannot convert " & $value &
|
||||
@@ -838,7 +855,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
|
||||
# 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 =
|
||||
@@ -948,7 +965,8 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||
|
||||
if result != nil:
|
||||
if result[0].kind != nkSym:
|
||||
internalError(c.config, "semOverloadedCallAnalyseEffects")
|
||||
if not (efDetermineType in flags and c.inGenericContext > 0):
|
||||
internalError(c.config, "semOverloadedCallAnalyseEffects")
|
||||
return
|
||||
let callee = result[0].sym
|
||||
case callee.kind
|
||||
@@ -977,13 +995,39 @@ proc bracketedMacro(n: PNode): PSym =
|
||||
if result.kind notin {skMacro, skTemplate}:
|
||||
result = nil
|
||||
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
for i in 1..<n.len:
|
||||
n[i].typ = semTypeNode(c, n[i], nil)
|
||||
proc finishOperand(c: PContext, a: PNode): PNode =
|
||||
if a.typ.isNil:
|
||||
result = c.semOperand(c, a, {efDetermineType})
|
||||
else:
|
||||
result = a
|
||||
# XXX tyGenericInst here?
|
||||
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
|
||||
#and tfUnresolved in result.typ.flags:
|
||||
let owner = result.typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % a.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % a.renderTree
|
||||
localError(c.config, a.info, err)
|
||||
considerGenSyms(c, result)
|
||||
|
||||
proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) =
|
||||
# this needs to be called to ensure that after overloading resolution every
|
||||
# argument has been sem'checked
|
||||
|
||||
# skip the first argument for operands of `[]` since it may be an unresolved
|
||||
# generic proc, which is handled in semMagic
|
||||
let start = 1 + ord(isBracketExpr)
|
||||
for i in start..<n.len:
|
||||
n[i] = finishOperand(c, n[i])
|
||||
|
||||
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
|
||||
return errorNode(c, n)
|
||||
if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0:
|
||||
return n
|
||||
|
||||
result = n
|
||||
|
||||
@@ -999,7 +1043,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
|
||||
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
|
||||
else:
|
||||
semFinishOperands(c, result)
|
||||
semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
|
||||
activate(c, result)
|
||||
fixAbstractType(c, result)
|
||||
analyseIfAddressTakenInCall(c, result)
|
||||
@@ -1039,7 +1083,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
if s != nil:
|
||||
setGenericParams(c, n[0])
|
||||
return semDirectOp(c, n, flags, expectedType)
|
||||
elif isSymChoice(n[0]):
|
||||
elif isSymChoice(n[0]) and nfDotField notin n.flags:
|
||||
# overloaded generic procs e.g. newSeq[int] can end up here
|
||||
return semDirectOp(c, n, flags, expectedType)
|
||||
|
||||
@@ -1048,7 +1092,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
|
||||
if t != nil and t.kind == tyTypeDesc:
|
||||
if n.len == 1: return semObjConstr(c, n, flags, expectedType)
|
||||
return semConv(c, n)
|
||||
return semConv(c, n, flags)
|
||||
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
@@ -1085,7 +1129,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
msg.addDeclaredLocMaybe(c.config, typ)
|
||||
localError(c.config, n.info, msg)
|
||||
return errorNode(c, n)
|
||||
result = nil
|
||||
else:
|
||||
result = m.call
|
||||
instGenericConvertersSons(c, result, m)
|
||||
@@ -1228,7 +1271,7 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
# This seems semantically correct and then we'll be able
|
||||
# to return the section symbol directly here
|
||||
let foundType = makeTypeDesc(c, def[2].typ)
|
||||
return newSymNode(copySym(def[0].sym, nextSymId c.idgen).linkTo(foundType), info)
|
||||
return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info)
|
||||
|
||||
of nkConstSection:
|
||||
for def in statement:
|
||||
@@ -1253,11 +1296,12 @@ proc readTypeParameter(c: PContext, typ: PType,
|
||||
return c.graph.emptyNode
|
||||
else:
|
||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||
return newSymNode(copySym(tParam.sym, nextSymId c.idgen).linkTo(foundTyp), info)
|
||||
return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
|
||||
|
||||
return nil
|
||||
|
||||
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
assert n.kind in nkIdentKinds + {nkDotExpr}
|
||||
let s = getGenSym(c, sym)
|
||||
case s.kind
|
||||
of skConst:
|
||||
@@ -1292,9 +1336,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
of skMacro, skTemplate:
|
||||
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
|
||||
(n.kind notin nkCallKinds and s.requiredParams > 0) or
|
||||
sfCustomPragma in sym.flags:
|
||||
# check if we cannot use alias syntax (no required args or generic params)
|
||||
if sfNoalias in s.flags:
|
||||
let info = getCallLineInfo(n)
|
||||
markUsed(c, info, s)
|
||||
onUse(info, s)
|
||||
@@ -1352,6 +1395,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
markUsed(c, n.info, s)
|
||||
onUse(n.info, s)
|
||||
result = newSymNode(s, n.info)
|
||||
of skModule:
|
||||
# make sure type is None and not nil for discard checking
|
||||
if efWantStmt in flags: s.typ = newTypeS(tyNone, c)
|
||||
markUsed(c, n.info, s)
|
||||
onUse(n.info, s)
|
||||
result = newSymNode(s, n.info)
|
||||
else:
|
||||
let info = getCallLineInfo(n)
|
||||
#if efInCall notin flags:
|
||||
@@ -1508,7 +1557,9 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
|
||||
flags.incl efCannotBeDotCall
|
||||
|
||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||
if isSymChoice(n[1]):
|
||||
if isSymChoice(n[1]) or
|
||||
# generics usually leave field names as symchoices, but not types
|
||||
(n[1].kind == nkSym and n[1].sym.kind == skType):
|
||||
result = newNodeI(nkDotCall, n.info)
|
||||
result.add n[1]
|
||||
result.add copyTree(n[0])
|
||||
@@ -1581,9 +1632,8 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result.add(x[0])
|
||||
return
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
# make sure we don't evaluate generic macros/templates
|
||||
n[0] = semExprWithType(c, n[0],
|
||||
{efNoEvaluateGeneric})
|
||||
# signal that generic parameters may be applied after
|
||||
n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric})
|
||||
var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned,
|
||||
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
|
||||
if arr.kind == tyStatic:
|
||||
@@ -1633,7 +1683,10 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
{tyInt..tyInt64}:
|
||||
let idx = getOrdValue(n[1])
|
||||
if idx >= 0 and idx < arr.len: n.typ = arr[toInt(idx)]
|
||||
else: localError(c.config, n.info, "invalid index value for tuple subscript")
|
||||
else:
|
||||
localError(c.config, n.info,
|
||||
"invalid index $1 in subscript for tuple of length $2" %
|
||||
[$idx, $arr.len])
|
||||
result = n
|
||||
else:
|
||||
result = nil
|
||||
@@ -1786,6 +1839,39 @@ proc goodLineInfo(arg: PNode): TLineInfo =
|
||||
else:
|
||||
arg.info
|
||||
|
||||
proc makeTupleAssignments(c: PContext; n: PNode): PNode =
|
||||
## expand tuple unpacking assignment into series of assignments
|
||||
##
|
||||
## mirrored with semstmts.makeVarTupleSection
|
||||
let lhs = n[0]
|
||||
let value = semExprWithType(c, n[1], {efTypeAllowed})
|
||||
if value.typ.kind != tyTuple:
|
||||
localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(value.typ, preferDesc)])
|
||||
elif lhs.len != value.typ.len:
|
||||
localError(c.config, n.info, errTupleUnpackingDifferentLengths %
|
||||
[$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
|
||||
temp.typ = value.typ
|
||||
temp.flags.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)
|
||||
vpart[0] = tempNode
|
||||
vpart[1] = c.graph.emptyNode
|
||||
vpart[2] = value
|
||||
v.add vpart
|
||||
result.add(v)
|
||||
|
||||
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
|
||||
else:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
|
||||
|
||||
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
checkSonsLen(n, 2, c.config)
|
||||
var a = n[0]
|
||||
@@ -1814,7 +1900,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
result.add(n[1])
|
||||
if mode == noOverloadedSubscript:
|
||||
bracketNotFoundError(c, result)
|
||||
return n
|
||||
return errorNode(c, n)
|
||||
else:
|
||||
result = semExprNoType(c, result)
|
||||
return result
|
||||
@@ -1824,11 +1910,11 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
result.add(n[1])
|
||||
return semExprNoType(c, result)
|
||||
of nkPar, nkTupleConstr:
|
||||
if a.len >= 2:
|
||||
if a.len >= 2 or a.kind == nkTupleConstr:
|
||||
# unfortunately we need to rewrite ``(x, y) = foo()`` already here so
|
||||
# that overloading of the assignment operator still works. Usually we
|
||||
# prefer to do these rewritings in transf.nim:
|
||||
return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
|
||||
return semStmt(c, makeTupleAssignments(c, n), {})
|
||||
else:
|
||||
a = semExprWithType(c, a, {efLValue})
|
||||
else:
|
||||
@@ -2077,10 +2163,11 @@ proc expectString(c: PContext, n: PNode): string =
|
||||
if n.kind in nkStrKinds:
|
||||
return n.strVal
|
||||
else:
|
||||
result = ""
|
||||
localError(c.config, n.info, errStringLiteralExpected)
|
||||
|
||||
proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
|
||||
result = newSym(kind, c.cache.idAnon, nextSymId c.idgen, getCurrOwner(c), info)
|
||||
result = newSym(kind, c.cache.idAnon, c.idgen, getCurrOwner(c), info)
|
||||
|
||||
proc semExpandToAst(c: PContext, n: PNode): PNode =
|
||||
let macroCall = n[1]
|
||||
@@ -2211,8 +2298,9 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||
dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
|
||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
||||
for i in 1..<ids.len:
|
||||
let typ = semExprWithType(c, quotes[i+1], {}).typ
|
||||
if tfTriggersCompileTime notin typ.flags and typ.kind != tyTypeDesc:
|
||||
let exp = semExprWithType(c, quotes[i+1], {})
|
||||
let typ = exp.typ
|
||||
if tfTriggersCompileTime notin typ.flags and typ.kind != tyStatic and exp.kind == nkSym and exp.sym.kind notin routineKinds + {skType}:
|
||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
|
||||
else:
|
||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
||||
@@ -2480,9 +2568,10 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
# If semCheck is set to false, ``when`` will return the verbatim AST of
|
||||
# the correct branch. Otherwise the AST will be passed through semStmt.
|
||||
result = nil
|
||||
let flags = if semCheck: {efWantStmt} else: {}
|
||||
|
||||
template setResult(e: untyped) =
|
||||
if semCheck: result = semExpr(c, e) # do not open a new scope!
|
||||
if semCheck: result = semExpr(c, e, flags) # do not open a new scope!
|
||||
else: result = e
|
||||
|
||||
# Check if the node is "when nimvm"
|
||||
@@ -2508,7 +2597,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
checkSonsLen(it, 2, c.config)
|
||||
if whenNimvm:
|
||||
if semCheck:
|
||||
it[1] = semExpr(c, it[1])
|
||||
it[1] = semExpr(c, it[1], flags)
|
||||
typ = commonType(c, typ, it[1].typ)
|
||||
result = n # when nimvm is not elimited until codegen
|
||||
else:
|
||||
@@ -2524,7 +2613,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
checkSonsLen(it, 1, c.config)
|
||||
if result == nil or whenNimvm:
|
||||
if semCheck:
|
||||
it[0] = semExpr(c, it[0])
|
||||
it[0] = semExpr(c, it[0], flags)
|
||||
typ = commonType(c, typ, it[0].typ)
|
||||
if result == nil:
|
||||
result = it[0]
|
||||
@@ -2556,34 +2645,39 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||
# only semantic checking for all elements, later type checking:
|
||||
var typ: PType = nil
|
||||
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 typ == nil:
|
||||
if doSetType:
|
||||
typ = skipTypes(n[i][1].typ,
|
||||
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
if expectedElementType == nil:
|
||||
expectedElementType = typ
|
||||
n[i].typ = n[i][2].typ # range node needs type too
|
||||
elif n[i].kind == nkRange:
|
||||
# already semchecked
|
||||
if typ == nil:
|
||||
if doSetType:
|
||||
typ = skipTypes(n[i][0].typ,
|
||||
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
if expectedElementType == nil:
|
||||
expectedElementType = typ
|
||||
else:
|
||||
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
|
||||
if typ == nil:
|
||||
if doSetType:
|
||||
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
if expectedElementType == nil:
|
||||
expectedElementType = typ
|
||||
if not isOrdinalType(typ, allowEnumWithHoles=true):
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
|
||||
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
||||
elif lengthOrd(c.config, typ) > MaxSetElements:
|
||||
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
||||
if doSetType:
|
||||
if not isOrdinalType(typ, allowEnumWithHoles=true):
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
|
||||
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
||||
elif isIntLit(typ):
|
||||
# set of int literal, use a default range smaller than the max range
|
||||
typ = makeRangeType(c, 0, DefaultSetElements-1, n.info)
|
||||
elif lengthOrd(c.config, typ) > MaxSetElements:
|
||||
message(c.config, n.info, warnAboveMaxSizeSet, "type '" &
|
||||
typeToString(typ, preferDesc) & "' is too big to be a `set` element, " &
|
||||
"assuming a range of 0.." & $(MaxSetElements - 1) &
|
||||
", explicitly write this range to get rid of warning")
|
||||
typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
|
||||
if expectedElementType == nil:
|
||||
expectedElementType = typ
|
||||
addSonSkipIntLit(result.typ, typ, c.idgen)
|
||||
for i in 0..<n.len:
|
||||
var m: PNode
|
||||
@@ -2826,6 +2920,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode =
|
||||
return result
|
||||
return nil
|
||||
|
||||
proc isOpenArraySym(x: PNode): bool =
|
||||
var x = x
|
||||
while true:
|
||||
case x.kind
|
||||
of {nkAddr, nkHiddenAddr}:
|
||||
x = x[0]
|
||||
of {nkHiddenStdConv, nkHiddenDeref}:
|
||||
x = x[1]
|
||||
else:
|
||||
break
|
||||
result = x.kind == nkSym
|
||||
|
||||
proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
|
||||
# This takes care of complicated signatures such as:
|
||||
# proc foo(a: int, b = a)
|
||||
@@ -2846,8 +2952,11 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
|
||||
if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
|
||||
let paramPos = defExpr.sym.position + 1
|
||||
|
||||
if call[paramPos].kind != nkSym:
|
||||
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), nextSymId c.idgen,
|
||||
if call[paramPos].skipAddr.kind != nkSym and not (
|
||||
skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
|
||||
isOpenArraySym(call[paramPos])
|
||||
):
|
||||
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen,
|
||||
c.p.owner, letSection.info, c.p.owner.options)
|
||||
hoistedVarSym.typ = call[paramPos].typ
|
||||
|
||||
@@ -2872,7 +2981,7 @@ proc getNilType(c: PContext): PType =
|
||||
result.align = c.config.target.ptrSize.int16
|
||||
c.nilTypeCache = result
|
||||
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
|
||||
var o: TOverloadIter
|
||||
var i = 0
|
||||
var a = initOverloadIter(o, c, n)
|
||||
@@ -2885,7 +2994,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
|
||||
if i <= 1:
|
||||
if sfGenSym notin s.flags:
|
||||
result = newSymNode(s, info)
|
||||
markUsed(c, info, s)
|
||||
markUsed(c, info, s, efInCall notin flags)
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
@@ -2960,6 +3069,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
else:
|
||||
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
|
||||
s = qualifiedLookUp(c, n, checks)
|
||||
if s == nil:
|
||||
return
|
||||
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
|
||||
case s.kind
|
||||
of skProc, skFunc, skMethod, skConverter, skIterator:
|
||||
@@ -2973,7 +3084,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
of skEnumField:
|
||||
result = enumFieldSymChoice(c, n, s)
|
||||
result = enumFieldSymChoice(c, n, s, flags)
|
||||
else:
|
||||
result = semSym(c, n, s, flags)
|
||||
if expectedType != nil and isSymChoice(result):
|
||||
@@ -2981,9 +3092,32 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
if result.kind == nkSym:
|
||||
result = semSym(c, result, result.sym, flags)
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
if nfOpenSym in n.flags:
|
||||
let id = newIdentNode(s.name, n.info)
|
||||
c.isAmbiguous = false
|
||||
let s2 = qualifiedLookUp(c, id, {})
|
||||
if s2 != nil and s2 != s and not c.isAmbiguous:
|
||||
# only consider symbols defined under current proc:
|
||||
var o = s2.owner
|
||||
while o != nil:
|
||||
if o == c.p.owner:
|
||||
if genericsOpenSym in c.features:
|
||||
result = semExpr(c, id, flags, expectedType)
|
||||
return
|
||||
else:
|
||||
message(c.config, n.info, warnGenericsIgnoredInjection,
|
||||
"a new symbol '" & s.name.s & "' has been injected during " &
|
||||
"instantiation of " & c.p.owner.name.s & ", " &
|
||||
"however " & getSymRepr(c.config, s) & " captured at " &
|
||||
"the proc declaration will be used instead; " &
|
||||
"either enable --experimental:genericsOpenSym to use the " &
|
||||
"injected symbol or `bind` this captured symbol explicitly")
|
||||
break
|
||||
o = o.owner
|
||||
# because of the changed symbol binding, this does not mean that we
|
||||
# don't have to check the symbol for semantics here again!
|
||||
result = semSym(c, n, n.sym, flags)
|
||||
result = semSym(c, n, s, flags)
|
||||
of nkEmpty, nkNone, nkCommentStmt, nkType:
|
||||
discard
|
||||
of nkNilLit:
|
||||
@@ -3001,7 +3135,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
expected.kind in {tyInt..tyInt64,
|
||||
tyUInt..tyUInt64,
|
||||
tyFloat..tyFloat128}):
|
||||
result.typ = expected
|
||||
if expected.kind in {tyFloat..tyFloat128}:
|
||||
n.transitionIntToFloatKind(nkFloatLit)
|
||||
changeType(c, result, expectedType, check=true)
|
||||
@@ -3066,8 +3199,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
c.isAmbiguous = false
|
||||
var s = qualifiedLookUp(c, n[0], mode)
|
||||
if s != nil:
|
||||
#if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr:
|
||||
# pretty.checkUse(n[0][1].info, s)
|
||||
case s.kind
|
||||
of skMacro, skTemplate:
|
||||
result = semDirectOp(c, n, flags, expectedType)
|
||||
@@ -3075,7 +3206,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
# XXX think about this more (``set`` procs)
|
||||
let ambig = c.isAmbiguous
|
||||
if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
|
||||
result = semConv(c, n, expectedType)
|
||||
result = semConv(c, n, flags, expectedType)
|
||||
elif ambig and n.len == 1:
|
||||
errorUseQualifier(c, n.info, s)
|
||||
elif n.len == 1:
|
||||
@@ -3245,7 +3376,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
|
||||
of nkDefer:
|
||||
if c.currentScope == c.topLevelScope:
|
||||
localError(c.config, n.info, "defer statement not supported at top level")
|
||||
openScope(c)
|
||||
n[0] = semExpr(c, n[0])
|
||||
closeScope(c)
|
||||
if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]):
|
||||
localError(c.config, n.info, "'defer' takes a 'void' expression")
|
||||
#localError(c.config, n.info, errGenerated, "'defer' not allowed in this context")
|
||||
|
||||
@@ -109,7 +109,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
|
||||
if trueSymbol == nil:
|
||||
localError(c.config, n.info, "system needs: 'true'")
|
||||
trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info)
|
||||
trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), c.idgen, getCurrOwner(c), n.info)
|
||||
trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
|
||||
|
||||
result[0] = newSymNode(trueSymbol, n.info)
|
||||
|
||||
@@ -230,7 +230,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
|
||||
of mDivF64:
|
||||
result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
|
||||
of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, idgen, g)
|
||||
of mIsNil:
|
||||
let val = a.kind == nkNilLit or
|
||||
# nil closures have the value (nil, nil)
|
||||
(a.typ != nil and skipTypes(a.typ, abstractRange).kind == tyProc and
|
||||
a.kind == nkTupleConstr and a.len == 2 and
|
||||
a[0].kind == nkNilLit and a[1].kind == nkNilLit)
|
||||
result = newIntNodeT(toInt128(ord(val)), n, idgen, g)
|
||||
of mLtI, mLtB, mLtEnum, mLtCh:
|
||||
result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g)
|
||||
of mLeI, mLeB, mLeEnum, mLeCh:
|
||||
|
||||
@@ -50,48 +50,49 @@ proc semGenericStmtScope(c: PContext, n: PNode,
|
||||
result = semGenericStmt(c, n, flags, ctx)
|
||||
closeScope(c)
|
||||
|
||||
template macroToExpand(s): untyped =
|
||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
|
||||
|
||||
template macroToExpandSym(s): untyped =
|
||||
sfCustomPragma notin s.flags and s.kind in {skMacro, skTemplate} and
|
||||
(s.typ.len == 1) and not fromDotExpr
|
||||
|
||||
template isMixedIn(sym): bool =
|
||||
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 =
|
||||
{withinMixin, withinConcept} * flags == {withinMixin} and s.id notin ctx.toBind
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var GenericCtx; flags: TSemGenericFlags,
|
||||
fromDotExpr=false): PNode =
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
incl(s.flags, sfUsed)
|
||||
template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) =
|
||||
if fromDotExpr:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
if result.kind == nkOpenSymChoice and result.len == 1:
|
||||
result.transitionSonsKind(nkClosedSymChoice)
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
if result.kind == nkSym and canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
case s.kind
|
||||
of skUnknown:
|
||||
# Introduced in this pass! Leave it as an identifier.
|
||||
result = n
|
||||
of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
of skTemplate:
|
||||
if macroToExpandSym(s):
|
||||
of skProc, skFunc, skMethod, skIterator, skConverter, skModule, skEnumField:
|
||||
maybeDotChoice(c, n, s, fromDotExpr)
|
||||
of skTemplate, skMacro:
|
||||
# alias syntax, see semSym for skTemplate, skMacro
|
||||
if sfNoalias notin s.flags and not fromDotExpr:
|
||||
onUse(n.info, s)
|
||||
result = semTemplateExpr(c, n, s, {efNoSemCheck})
|
||||
case s.kind
|
||||
of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck})
|
||||
of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck})
|
||||
else: discard # unreachable
|
||||
c.friendModules.add(s.owner.getModule)
|
||||
result = semGenericStmt(c, result, {}, ctx)
|
||||
discard c.friendModules.pop()
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
of skMacro:
|
||||
if macroToExpandSym(s):
|
||||
onUse(n.info, s)
|
||||
result = semMacroExpr(c, n, n, s, {efNoSemCheck})
|
||||
c.friendModules.add(s.owner.getModule)
|
||||
result = semGenericStmt(c, result, {}, ctx)
|
||||
discard c.friendModules.pop()
|
||||
else:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
maybeDotChoice(c, n, s, fromDotExpr)
|
||||
of skGenericParam:
|
||||
if s.typ != nil and s.typ.kind == tyStatic:
|
||||
if s.typ.n != nil:
|
||||
@@ -100,6 +101,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result = n
|
||||
else:
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
@@ -108,13 +112,17 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
if (s.typ != nil) and
|
||||
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
|
||||
result = newSymNodeTypeDesc(s, c.idgen, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
else:
|
||||
result = n
|
||||
onUse(n.info, s)
|
||||
of skEnumField:
|
||||
result = symChoice(c, n, s, scOpen)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
if canOpenSym(result.sym):
|
||||
result.flags.incl nfOpenSym
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
|
||||
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
@@ -122,7 +130,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
result = n
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
var amb = false
|
||||
var s = searchInScopes(c, ident, amb).skipAlias(n, c.config)
|
||||
var s = searchInScopes(c, ident, amb)
|
||||
if s == nil:
|
||||
s = strTableGet(c.pureEnumFields, ident)
|
||||
#if s != nil and contains(c.ambiguousSymbols, s.id):
|
||||
@@ -145,7 +153,8 @@ proc newDot(n, b: PNode): PNode =
|
||||
result.add(b)
|
||||
|
||||
proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
ctx: var GenericCtx; isMacro: var bool): PNode =
|
||||
ctx: var GenericCtx; isMacro: var bool;
|
||||
inCall = false): PNode =
|
||||
assert n.kind == nkDotExpr
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
|
||||
@@ -153,28 +162,40 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
if s != nil:
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
else:
|
||||
n[0] = semGenericStmt(c, n[0], flags, ctx)
|
||||
result = n
|
||||
let n = n[1]
|
||||
let ident = considerQuotedIdent(c, n)
|
||||
var candidates = searchInScopesFilterBy(c, ident, routineKinds) # .skipAlias(n, c.config)
|
||||
# could be type conversion if like a.T and not a.T()
|
||||
let symKinds = if inCall: routineKinds else: routineKinds+{skType}
|
||||
var candidates = searchInScopesFilterBy(c, ident, symKinds)
|
||||
if candidates.len > 0:
|
||||
let s = candidates[0] # XXX take into account the other candidates!
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
if withinBind in flags or s.id in ctx.toBind:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
if s.kind == skType: # don't put types in sym choice
|
||||
result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true))
|
||||
else:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
elif s.isMixedIn:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
if s.kind == skType and candidates.len > 1:
|
||||
var ambig = false
|
||||
let s2 = searchInScopes(c, ident, ambig)
|
||||
if ambig:
|
||||
# this is a type conversion like a.T where T is ambiguous with
|
||||
# other types or routines
|
||||
# in regular code, this never considers a type conversion and
|
||||
# skips to routine overloading
|
||||
# so symchoices are used which behave similarly with type symbols
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
return
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||
if syms.kind == nkSym:
|
||||
let choice = symChoice(c, n, s, scForceOpen)
|
||||
choice.transitionSonsKind(nkClosedSymChoice)
|
||||
result = newDot(result, choice)
|
||||
else:
|
||||
result = newDot(result, syms)
|
||||
result = newDot(result, syms)
|
||||
|
||||
proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
|
||||
let s = newSymS(skUnknown, getIdentNode(c, n), c)
|
||||
@@ -182,6 +203,18 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
|
||||
styleCheckDef(c, n.info, s, kind)
|
||||
onDef(n.info, s)
|
||||
|
||||
proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) =
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
if inCall:
|
||||
addTempDecl(c, n, kind)
|
||||
of nkCallKinds:
|
||||
for s in n:
|
||||
addTempDeclToIdents(c, s, kind, true)
|
||||
else:
|
||||
for s in n:
|
||||
addTempDeclToIdents(c, s, kind, inCall)
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
|
||||
result = n
|
||||
@@ -245,21 +278,14 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
else: scOpen
|
||||
let sc = symChoice(c, fn, s, whichChoice)
|
||||
case s.kind
|
||||
of skMacro:
|
||||
if macroToExpand(s) and sc.safeLen <= 1:
|
||||
of skMacro, skTemplate:
|
||||
# unambiguous macros/templates are expanded if all params are untyped
|
||||
if sfAllUntyped in s.flags and sc.safeLen <= 1:
|
||||
onUse(fn.info, s)
|
||||
result = semMacroExpr(c, n, n, s, {efNoSemCheck})
|
||||
c.friendModules.add(s.owner.getModule)
|
||||
result = semGenericStmt(c, result, flags, ctx)
|
||||
discard c.friendModules.pop()
|
||||
else:
|
||||
n[0] = sc
|
||||
result = n
|
||||
mixinContext = true
|
||||
of skTemplate:
|
||||
if macroToExpand(s) and sc.safeLen <= 1:
|
||||
onUse(fn.info, s)
|
||||
result = semTemplateExpr(c, n, s, {efNoSemCheck})
|
||||
case s.kind
|
||||
of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck})
|
||||
of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck})
|
||||
else: discard # unreachable
|
||||
c.friendModules.add(s.owner.getModule)
|
||||
result = semGenericStmt(c, result, flags, ctx)
|
||||
discard c.friendModules.pop()
|
||||
@@ -296,7 +322,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
onUse(fn.info, s)
|
||||
first = 1
|
||||
elif fn.kind == nkDotExpr:
|
||||
result[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
|
||||
result[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext, inCall = true)
|
||||
first = 1
|
||||
# Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
|
||||
# in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
|
||||
@@ -361,7 +387,9 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
var a = n[i]
|
||||
checkMinSonsLen(a, 1, c.config)
|
||||
for j in 0..<a.len-1:
|
||||
a[j] = semGenericStmt(c, a[j], flags, ctx)
|
||||
a[j] = semGenericStmt(c, a[j], flags+{withinMixin}, ctx)
|
||||
addTempDeclToIdents(c, a[j], skVar, false)
|
||||
|
||||
a[^1] = semGenericStmtScope(c, a[^1], flags, ctx)
|
||||
closeScope(c)
|
||||
of nkForStmt, nkParForStmt:
|
||||
@@ -458,8 +486,47 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
of nkIdent: a = n[i]
|
||||
else: illFormedAst(n, c.config)
|
||||
addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
|
||||
of nkObjectTy, nkTupleTy, nkTupleClassTy:
|
||||
discard
|
||||
of nkTupleTy:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
|
||||
a[^1] = semGenericStmt(c, a[^1], flags, ctx)
|
||||
for j in 0..<a.len-2:
|
||||
addTempDecl(c, getIdentNode(c, a[j]), skField)
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
of nkObjectTy:
|
||||
if n.len > 0:
|
||||
openScope(c)
|
||||
for i in 0..<n.len:
|
||||
result[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
closeScope(c)
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
|
||||
a[^1] = semGenericStmt(c, a[^1], flags, ctx)
|
||||
for j in 0..<a.len-2:
|
||||
addTempDecl(c, getIdentNode(c, a[j]), skField)
|
||||
of nkRecCase, nkRecWhen:
|
||||
n[i] = semGenericStmt(c, a, flags, ctx)
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
of nkRecCase:
|
||||
checkSonsLen(n[0], 3, c.config)
|
||||
n[0][^2] = semGenericStmt(c, n[0][^2], flags+{withinTypeDesc}, ctx)
|
||||
n[0][^1] = semGenericStmt(c, n[0][^1], flags, ctx)
|
||||
addTempDecl(c, getIdentNode(c, n[0][0]), skField)
|
||||
for i in 1..<n.len:
|
||||
n[i] = semGenericStmt(c, n[i], flags, ctx)
|
||||
of nkFormalParams:
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
for i in 1..<n.len:
|
||||
@@ -484,7 +551,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
flags, ctx)
|
||||
if n[paramsPos].kind != nkEmpty:
|
||||
if n[paramsPos][0].kind != nkEmpty:
|
||||
addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info))
|
||||
addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, nil, n.info))
|
||||
n[paramsPos] = semGenericStmt(c, n[paramsPos], flags, ctx)
|
||||
n[pragmasPos] = semGenericStmt(c, n[pragmasPos], flags, ctx)
|
||||
var body: PNode
|
||||
|
||||
@@ -45,7 +45,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
|
||||
var q = a.sym
|
||||
if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses:
|
||||
let symKind = if q.typ.kind == tyStatic: skConst else: skType
|
||||
var s = newSym(symKind, q.name, nextSymId(c.idgen), getCurrOwner(c), q.info)
|
||||
var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
|
||||
s.flags.incl {sfUsed, sfFromGeneric}
|
||||
var t = PType(idTableGet(pt, q.typ))
|
||||
if t == nil:
|
||||
@@ -60,6 +60,12 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
|
||||
elif t.kind in {tyGenericParam, tyConcept}:
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif isUnresolvedStatic(t) and c.inGenericContext == 0 and
|
||||
c.matchedConcept == nil:
|
||||
# generic/concept type bodies will try to instantiate static values but
|
||||
# won't actually use them
|
||||
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
|
||||
t = errorType(c)
|
||||
elif t.kind == tyGenericInvocation:
|
||||
#t = instGenericContainer(c, a, t)
|
||||
t = generateTypeInstance(c, pt, a, t)
|
||||
@@ -100,7 +106,7 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable
|
||||
n.sym = x
|
||||
elif s.owner == nil or s.owner.kind == skPackage:
|
||||
#echo "copied this ", s.name.s
|
||||
x = copySym(s, nextSymId c.idgen)
|
||||
x = copySym(s, c.idgen)
|
||||
x.owner = owner
|
||||
idTablePut(symMap, s, x)
|
||||
n.sym = x
|
||||
@@ -128,7 +134,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
||||
idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
|
||||
freshGenSyms(c, b, result, orig, symMap)
|
||||
|
||||
if sfBorrow notin orig.flags:
|
||||
if sfBorrow notin orig.flags:
|
||||
# We do not want to generate a body for generic borrowed procs.
|
||||
# As body is a sym to the borrowed proc.
|
||||
let resultType = # todo probably refactor it into a function
|
||||
@@ -193,7 +199,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||
var param: PSym
|
||||
|
||||
template paramSym(kind): untyped =
|
||||
newSym(kind, genParam.sym.name, nextSymId c.idgen, genericTyp.sym, genParam.sym.info)
|
||||
newSym(kind, genParam.sym.name, c.idgen, genericTyp.sym, genParam.sym.info)
|
||||
|
||||
if genParam.kind == tyStatic:
|
||||
param = paramSym skConst
|
||||
@@ -263,7 +269,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam, nextSymId c.idgen)
|
||||
let param = copySym(oldParam, c.idgen)
|
||||
param.owner = prc
|
||||
param.typ = result[i]
|
||||
|
||||
@@ -272,9 +278,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
# call head symbol, because this leads to infinite recursion.
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind == nkCall:
|
||||
if def.kind in nkCallKinds:
|
||||
for i in 1..<def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i])
|
||||
def[i] = replaceTypeVarsN(cl, def[i], 1)
|
||||
|
||||
def = semExprWithType(c, def)
|
||||
if def.referencesAnotherParam(getCurrOwner(c)):
|
||||
@@ -340,7 +346,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
c.matchedConcept = nil
|
||||
let oldScope = c.currentScope
|
||||
while not isTopLevel(c): c.currentScope = c.currentScope.parent
|
||||
result = copySym(fn, nextSymId c.idgen)
|
||||
result = copySym(fn, c.idgen)
|
||||
incl(result.flags, sfFromGeneric)
|
||||
result.owner = fn
|
||||
result.ast = n
|
||||
@@ -352,7 +358,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
|
||||
openScope(c)
|
||||
let gp = n[genericParamsPos]
|
||||
internalAssert c.config, gp.kind == nkGenericParams
|
||||
if gp.kind != nkGenericParams:
|
||||
# bug #22137
|
||||
globalError(c.config, info, "generic instantiation too nested")
|
||||
n[namePos] = newSymNode(result)
|
||||
pushInfoContext(c.config, info, fn.detailedInfo)
|
||||
var entry = TInstantiation.new
|
||||
@@ -362,10 +370,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
# see ttypeor.nim test.
|
||||
var i = 0
|
||||
newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
|
||||
# let param instantiation know we are in a concept for unresolved statics:
|
||||
c.matchedConcept = oldMatchedConcept
|
||||
for s in instantiateGenericParamList(c, gp, pt):
|
||||
addDecl(c, s)
|
||||
entry.concreteTypes[i] = s.typ
|
||||
inc i
|
||||
c.matchedConcept = nil
|
||||
pushProcCon(c, result)
|
||||
instantiateProcType(c, pt, result, info)
|
||||
for j in 1..<result.typ.len:
|
||||
@@ -388,8 +399,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
pragma(c, result, n[pragmasPos], allRoutinePragmas)
|
||||
if isNil(n[bodyPos]):
|
||||
n[bodyPos] = copyTree(getBody(c.graph, fn))
|
||||
if c.inGenericContext == 0:
|
||||
instantiateBody(c, n, fn.typ.n, result, fn)
|
||||
instantiateBody(c, n, fn.typ.n, result, fn)
|
||||
sideEffectsCheck(c, result)
|
||||
if result.magic notin {mSlice, mTypeOf}:
|
||||
# 'toOpenArray' is special and it is allowed to return 'openArray':
|
||||
|
||||
@@ -21,7 +21,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
|
||||
asgnExpr.typ = typ
|
||||
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
|
||||
while true:
|
||||
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
|
||||
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
|
||||
let base = t[0]
|
||||
if base == nil:
|
||||
break
|
||||
@@ -66,7 +66,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
|
||||
bracketNotFoundError(c, x)
|
||||
#localError(c.config, n.info, "could not resolve: " & $n)
|
||||
result = n
|
||||
result = errorNode(c, n)
|
||||
|
||||
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
|
||||
# rewrite `[]=`(a, i, x) back to ``a[i] = x``.
|
||||
@@ -210,6 +210,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
arg = arg.base.skipTypes(skippedTypes + {tyGenericInst})
|
||||
if not rec: break
|
||||
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
|
||||
of "rangeBase":
|
||||
# return the base type of a range type
|
||||
var arg = operand.skipTypes({tyGenericInst})
|
||||
assert arg.kind == tyRange
|
||||
result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info)
|
||||
of "isCyclic":
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
let isCyclic = canFormAcycle(c.graph, operand)
|
||||
result = newIntNodeT(toInt128(ord(isCyclic)), traitCall, c.idgen, c.graph)
|
||||
else:
|
||||
localError(c.config, traitCall.info, "unknown trait: " & s)
|
||||
result = newNodeI(nkEmpty, traitCall.info)
|
||||
@@ -415,14 +424,14 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
#if n.kind == nkDerefExpr and sameType(n[0].typ, old):
|
||||
# result =
|
||||
|
||||
result = copySym(orig, nextSymId c.idgen)
|
||||
result = copySym(orig, c.idgen)
|
||||
result.info = info
|
||||
result.flags.incl sfFromGeneric
|
||||
result.owner = orig
|
||||
let origParamType = orig.typ[1]
|
||||
let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
|
||||
let oldParam = orig.typ.n[1].sym
|
||||
let newParam = newSym(skParam, oldParam.name, nextSymId c.idgen, result, result.info)
|
||||
let newParam = newSym(skParam, oldParam.name, c.idgen, result, result.info)
|
||||
newParam.typ = newParamType
|
||||
# proc body:
|
||||
result.ast = transform(c, orig.ast, origParamType, newParamType, oldParam, newParam)
|
||||
@@ -488,8 +497,8 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
|
||||
discard "already turned this one into a finalizer"
|
||||
else:
|
||||
let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), nextSymId c.idgen, fin.owner, fin.info)
|
||||
let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, nextSymId c.idgen))
|
||||
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[1]
|
||||
wrapperSym.flags.incl sfUsed
|
||||
|
||||
@@ -510,12 +519,16 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
|
||||
selfPtr.add transFormedSym.ast[bodyPos][1]
|
||||
selfPtr.typ = selfSymbolType
|
||||
transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor)
|
||||
# TODO: suppress var destructor warnings; if newFinalizer is not
|
||||
# TODO: deprecated, try to implement plain T destructor
|
||||
bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
|
||||
result = addDefaultFieldForNew(c, n)
|
||||
|
||||
proc semPrivateAccess(c: PContext, n: PNode): PNode =
|
||||
let t = n[1].typ[0].toObjectFromRefPtrGeneric
|
||||
c.currentScope.allowPrivateAccess.add t.sym
|
||||
if t.kind == tyObject:
|
||||
assert t.sym != nil
|
||||
c.currentScope.allowPrivateAccess.add t.sym
|
||||
result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid))
|
||||
|
||||
proc checkDefault(c: PContext, n: PNode): PNode =
|
||||
@@ -598,12 +611,28 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
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[1].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 mTrace:
|
||||
result = n
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
let op = getAttachedOp(c.graph, t, attachedTrace)
|
||||
if op != nil:
|
||||
result[0] = newSymNode(op)
|
||||
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
|
||||
of mUnown:
|
||||
result = semUnown(c, n)
|
||||
of mExists, mForall:
|
||||
@@ -620,7 +649,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mDefault:
|
||||
result = checkDefault(c, n)
|
||||
let typ = result[^1].typ.skipTypes({tyTypeDesc})
|
||||
let defaultExpr = defaultNodeField(c, result[^1], typ)
|
||||
let defaultExpr = defaultNodeField(c, result[^1], typ, false)
|
||||
if defaultExpr != nil:
|
||||
result = defaultExpr
|
||||
of mZeroDefault:
|
||||
@@ -639,5 +668,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result = n
|
||||
if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and expectedType.kind == tySequence and result.typ[0].kind == tyEmpty:
|
||||
result.typ = expectedType # type inference for empty sequence # bug #21377
|
||||
of mEnsureMove:
|
||||
result = n
|
||||
if n[1].kind in {nkStmtListExpr, nkBlockExpr,
|
||||
nkIfExpr, nkCaseStmt, nkTryStmt}:
|
||||
localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'")
|
||||
else:
|
||||
result = n
|
||||
|
||||
@@ -21,6 +21,7 @@ type
|
||||
# set this to true while visiting
|
||||
# parent types.
|
||||
missingFields: seq[PSym] # Fields that the user failed to specify
|
||||
checkDefault: bool # Checking defaults
|
||||
|
||||
InitStatus = enum # This indicates the result of object construction
|
||||
initUnknown
|
||||
@@ -86,10 +87,13 @@ proc semConstrField(c: PContext, flags: TExprFlags,
|
||||
var initValue = semExprFlagDispatched(c, assignment[1], flags, field.typ)
|
||||
if initValue != nil:
|
||||
initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info)
|
||||
initValue.flags.incl nfSkipFieldChecking
|
||||
assignment[0] = newSymNode(field)
|
||||
assignment[1] = initValue
|
||||
assignment.flags.incl nfSem
|
||||
return initValue
|
||||
result = initValue
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
|
||||
isStmtBranch: bool): IntSet =
|
||||
@@ -182,6 +186,23 @@ proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
|
||||
asgnExpr.typ = recTyp
|
||||
defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
|
||||
|
||||
proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
|
||||
constrCtx: var ObjConstrContext, flags: TExprFlags) =
|
||||
# All bets are off. If any of the branches has a mandatory
|
||||
# fields we must produce an error:
|
||||
for i in 1..<n.len:
|
||||
let branchNode = n[i]
|
||||
if branchNode != nil:
|
||||
let oldCheckDefault = constrCtx.checkDefault
|
||||
constrCtx.checkDefault = true
|
||||
let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
|
||||
constrCtx.checkDefault = oldCheckDefault
|
||||
if len(defaults) > 0:
|
||||
localError(c.config, discriminatorVal.info, "branch initialization " &
|
||||
"with a runtime discriminator is not supported " &
|
||||
"for a branch whose fields have default values.")
|
||||
discard collectMissingCaseFields(c, n[i], constrCtx, @[])
|
||||
|
||||
proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
|
||||
case n.kind
|
||||
@@ -320,13 +341,27 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
discriminator.sym,
|
||||
constrCtx.initExpr)
|
||||
if discriminatorVal == nil:
|
||||
# None of the branches were explicitly selected by the user and no
|
||||
# value was given to the discrimator. We can assume that it will be
|
||||
# initialized to zero and this will select a particular branch as
|
||||
# a result:
|
||||
let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
|
||||
let matchedBranch = n.pickCaseBranch defaultValue
|
||||
discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
|
||||
if discriminator.sym.ast != nil:
|
||||
# branch is selected by the default field value of discriminator
|
||||
let discriminatorDefaultVal = discriminator.sym.ast
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal)
|
||||
if discriminatorDefaultVal.kind == nkIntLit:
|
||||
let matchedBranch = n.pickCaseBranch discriminatorDefaultVal
|
||||
if matchedBranch != nil:
|
||||
let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
|
||||
result.defaults.add defaults
|
||||
collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
|
||||
else:
|
||||
collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags)
|
||||
else:
|
||||
# None of the branches were explicitly selected by the user and no
|
||||
# value was given to the discrimator. We can assume that it will be
|
||||
# initialized to zero and this will select a particular branch as
|
||||
# a result:
|
||||
let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
|
||||
let matchedBranch = n.pickCaseBranch defaultValue
|
||||
discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
|
||||
else:
|
||||
result.status = initPartial
|
||||
if discriminatorVal.kind == nkIntLit:
|
||||
@@ -338,10 +373,8 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
result.defaults.add defaults
|
||||
collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
|
||||
else:
|
||||
# All bets are off. If any of the branches has a mandatory
|
||||
# fields we must produce an error:
|
||||
for i in 1..<n.len:
|
||||
discard collectMissingCaseFields(c, n[i], constrCtx, @[])
|
||||
collectBranchFields(c, n, discriminatorVal, constrCtx, flags)
|
||||
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
let e = semConstrField(c, flags, field, constrCtx.initExpr)
|
||||
@@ -352,7 +385,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||
else:
|
||||
if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
|
||||
let defaultExpr = defaultNodeField(c, n)
|
||||
let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
|
||||
if defaultExpr != nil:
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
|
||||
@@ -375,7 +408,9 @@ proc semConstructTypeAux(c: PContext,
|
||||
if status in {initPartial, initNone, initUnknown}:
|
||||
discard collectMissingFields(c, t.n, constrCtx, result.defaults)
|
||||
let base = t[0]
|
||||
if base == nil: break
|
||||
if base == nil or base.id == t.id or
|
||||
base.kind in { tyRef, tyPtr } and base[0].id == t.id:
|
||||
break
|
||||
t = skipTypes(base, skipPtrs)
|
||||
if t.kind != tyObject:
|
||||
# XXX: This is not supposed to happen, but apparently
|
||||
@@ -437,7 +472,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
result.typ.flags.incl tfHasOwned
|
||||
if t.kind != tyObject:
|
||||
return localErrorNode(c, result, if t.kind != tyGenericBody:
|
||||
"object constructor needs an object type".dup(addDeclaredLoc(c.config, t))
|
||||
"object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))
|
||||
else: "cannot instantiate: '" &
|
||||
typeToString(t, preferDesc) &
|
||||
"'; the object's generic parameters cannot be inferred and must be explicitly given"
|
||||
|
||||
@@ -486,7 +486,7 @@ proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): P
|
||||
checkArgs(a, body)
|
||||
|
||||
var varSection = newNodeI(nkVarSection, n.info)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "barrier"), nextSymId idgen, owner, 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)
|
||||
let tempNode = newSymNode(temp)
|
||||
|
||||
@@ -90,7 +90,10 @@ const
|
||||
errLetNeedsInit = "'let' symbol requires an initialization"
|
||||
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||
if typ == nil: return
|
||||
if typ == nil or sfGeneratedOp in tracked.owner.flags:
|
||||
# don't create type bound ops for anything in a function with a `nodestroy` pragma
|
||||
# bug #21987
|
||||
return
|
||||
when false:
|
||||
let realType = typ.skipTypes(abstractInst)
|
||||
if realType.kind == tyRef and
|
||||
@@ -233,7 +236,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
|
||||
if reason.kind == nkSym:
|
||||
a.owner.gcUnsafetyReason = reason.sym
|
||||
else:
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextSymId a.c.idgen,
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, a.c.idgen,
|
||||
a.owner, reason.info, {})
|
||||
|
||||
proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) =
|
||||
@@ -246,7 +249,7 @@ proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) =
|
||||
sym = reason.sym
|
||||
else:
|
||||
let kind = if reason.kind == nkHiddenDeref: skParam else: skUnknown
|
||||
sym = newSym(kind, a.owner.name, nextSymId a.c.idgen, a.owner, reason.info, {})
|
||||
sym = newSym(kind, a.owner.name, a.c.idgen, a.owner, reason.info, {})
|
||||
else:
|
||||
sym = reason
|
||||
a.c.sideEffects.mgetOrPut(a.owner.id, @[]).add (useLoc, sym)
|
||||
@@ -353,12 +356,15 @@ proc useVar(a: PEffects, n: PNode) =
|
||||
type
|
||||
TIntersection = seq[tuple[id, count: int]] # a simple count table
|
||||
|
||||
proc addToIntersection(inter: var TIntersection, s: int) =
|
||||
proc addToIntersection(inter: var TIntersection, s: int, zeroInit: bool) =
|
||||
for j in 0..<inter.len:
|
||||
if s == inter[j].id:
|
||||
inc inter[j].count
|
||||
return
|
||||
inter.add((id: s, count: 1))
|
||||
if zeroInit:
|
||||
inter.add((id: s, count: 0))
|
||||
else:
|
||||
inter.add((id: s, count: 1))
|
||||
|
||||
proc throws(tracked, n, orig: PNode) =
|
||||
if n.typ == nil or n.typ.kind != tyError:
|
||||
@@ -396,7 +402,7 @@ proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
|
||||
if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): return
|
||||
|
||||
if e.typ != nil:
|
||||
if optNimV1Emulation in a.config.globalOptions or not isDefectException(e.typ):
|
||||
if not isDefectException(e.typ):
|
||||
throws(a.exc, e, comesFrom)
|
||||
|
||||
proc addTag(a: PEffects, e, comesFrom: PNode) =
|
||||
@@ -462,7 +468,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
track(tracked, n[0])
|
||||
dec tracked.inTryStmt
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], false)
|
||||
|
||||
var branches = 1
|
||||
var hasFinally = false
|
||||
@@ -497,7 +503,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
tracked.init.add b[j][2].sym.id
|
||||
track(tracked, b[^1])
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], false)
|
||||
else:
|
||||
setLen(tracked.init, oldState)
|
||||
track(tracked, b[^1])
|
||||
@@ -693,9 +699,11 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||
addCaseBranchFacts(tracked.guards, n, i)
|
||||
for i in 0..<branch.len:
|
||||
track(tracked, branch[i])
|
||||
if not breaksBlock(branch.lastSon): inc toCover
|
||||
let hasBreaksBlock = breaksBlock(branch.lastSon)
|
||||
if not hasBreaksBlock:
|
||||
inc toCover
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
|
||||
|
||||
setLen(tracked.init, oldState)
|
||||
if not stringCase or lastSon(n).kind == nkElse:
|
||||
@@ -715,9 +723,11 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
var inter: TIntersection = @[]
|
||||
var toCover = 0
|
||||
track(tracked, n[0][1])
|
||||
if not breaksBlock(n[0][1]): inc toCover
|
||||
let hasBreaksBlock = breaksBlock(n[0][1])
|
||||
if not hasBreaksBlock:
|
||||
inc toCover
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
|
||||
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
@@ -729,9 +739,12 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
setLen(tracked.init, oldState)
|
||||
for i in 0..<branch.len:
|
||||
track(tracked, branch[i])
|
||||
if not breaksBlock(branch.lastSon): inc toCover
|
||||
let hasBreaksBlock = breaksBlock(branch.lastSon)
|
||||
if not hasBreaksBlock:
|
||||
inc toCover
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i])
|
||||
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
|
||||
|
||||
setLen(tracked.init, oldState)
|
||||
if lastSon(n).len == 1:
|
||||
for id, count in items(inter):
|
||||
@@ -833,7 +846,6 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
# and it's not a recursive call:
|
||||
if not (a.kind == nkSym and a.sym == tracked.owner):
|
||||
markSideEffect(tracked, a, n.info)
|
||||
|
||||
# p's effects are ours too:
|
||||
var a = n[0]
|
||||
#if canRaise(a):
|
||||
@@ -876,7 +888,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
|
||||
mergeRaises(tracked, effectList[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList[tagEffects], n)
|
||||
gcsafeAndSideeffectCheck()
|
||||
if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst}:
|
||||
if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst, mType}:
|
||||
for i in 1..<n.len:
|
||||
trackOperandForIndirectCall(tracked, n[i], op, i, a)
|
||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
@@ -1117,7 +1129,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
elif child.kind == nkVarTuple:
|
||||
for i in 0..<child.len-1:
|
||||
if child[i].kind == nkEmpty or
|
||||
child[i].kind == nkSym and child[i].sym.name.s == "_":
|
||||
child[i].kind == nkSym and child[i].sym.name.id == ord(wUnderscore):
|
||||
continue
|
||||
varDecl(tracked, child[i])
|
||||
if last.kind != nkEmpty:
|
||||
@@ -1221,6 +1233,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of nkTupleConstr:
|
||||
for i in 0..<n.len:
|
||||
track(tracked, n[i])
|
||||
notNilCheck(tracked, n[i].skipColon, n[i].typ)
|
||||
if tracked.owner.kind != skMacro:
|
||||
if n[i].kind == nkExprColonExpr:
|
||||
createTypeBoundOps(tracked, n[i][0].typ, n.info)
|
||||
@@ -1316,6 +1329,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
|
||||
proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
|
||||
if spec.typ.kind == tyOr:
|
||||
result = false
|
||||
for t in spec.typ.sons:
|
||||
if safeInheritanceDiff(g.excType(real), t) <= 0:
|
||||
return true
|
||||
@@ -1480,7 +1494,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
let param = params[i].sym
|
||||
let typ = param.typ
|
||||
if isSinkTypeForParam(typ) or
|
||||
(t.config.selectedGC in {gcArc, gcOrc} and
|
||||
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
|
||||
createTypeBoundOps(t, typ, param.info)
|
||||
if isOutParam(typ) and param.id notin t.init:
|
||||
@@ -1499,7 +1513,8 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
let p = s.ast[pragmasPos]
|
||||
let raisesSpec = effectSpec(p, wRaises)
|
||||
if not isNil(raisesSpec):
|
||||
checkRaisesSpec(g, false, raisesSpec, t.exc, "can raise an unlisted exception: ",
|
||||
let useWarning = s.name.s == "=destroy"
|
||||
checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
|
||||
hints=on, subtypeRelation, hintsArg=s.ast[0])
|
||||
# after the check, use the formal spec:
|
||||
effects[exceptionEffects] = raisesSpec
|
||||
|
||||
@@ -161,18 +161,24 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
|
||||
if result.typ.kind == tyNone:
|
||||
localError(c.config, result.info, "expression has no type: " &
|
||||
renderTree(result, {renderNoComments}))
|
||||
var n = result
|
||||
while n.kind in skipForDiscardable:
|
||||
if n.kind == nkTryStmt: n = n[0]
|
||||
else: n = n.lastSon
|
||||
var s = "expression '" & $n & "' is of type '" &
|
||||
result.typ.typeToString & "' and has to be used (or discarded)"
|
||||
if result.info.line != n.info.line or
|
||||
result.info.fileIndex != n.info.fileIndex:
|
||||
s.add "; start of expression here: " & c.config$result.info
|
||||
if result.typ.kind == tyProc:
|
||||
s.add "; for a function call use ()"
|
||||
localError(c.config, n.info, s)
|
||||
else:
|
||||
var n = result
|
||||
while n.kind in skipForDiscardable:
|
||||
if n.kind == nkTryStmt: n = n[0]
|
||||
else: n = n.lastSon
|
||||
|
||||
# Ignore noreturn procs since they don't have a type
|
||||
if n.endsInNoReturn:
|
||||
return
|
||||
|
||||
var s = "expression '" & $n & "' is of type '" &
|
||||
result.typ.typeToString & "' and has to be used (or discarded)"
|
||||
if result.info.line != n.info.line or
|
||||
result.info.fileIndex != n.info.fileIndex:
|
||||
s.add "; start of expression here: " & c.config$result.info
|
||||
if result.typ.kind == tyProc:
|
||||
s.add "; for a function call use ()"
|
||||
localError(c.config, n.info, s)
|
||||
|
||||
proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
result = n
|
||||
@@ -186,13 +192,15 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil):
|
||||
it[0] = forceBool(c, semExprWithType(c, it[0], expectedType = getSysType(c.graph, n.info, tyBool)))
|
||||
it[1] = semExprBranch(c, it[1], flags, expectedType)
|
||||
typ = commonType(c, typ, it[1])
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(it[1]):
|
||||
expectedType = typ
|
||||
closeScope(c)
|
||||
elif it.len == 1:
|
||||
hasElse = true
|
||||
it[0] = semExprBranchScope(c, it[0], expectedType)
|
||||
typ = commonType(c, typ, it[0])
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(it[0]):
|
||||
expectedType = typ
|
||||
else: illFormedAst(it, c.config)
|
||||
if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
|
||||
(not hasElse and efInTypeof notin flags):
|
||||
@@ -232,8 +240,9 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
var typ = commonTypeBegin
|
||||
var expectedType = expectedType
|
||||
n[0] = semExprBranchScope(c, n[0], expectedType)
|
||||
typ = commonType(c, typ, n[0].typ)
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(n[0]):
|
||||
typ = commonType(c, typ, n[0].typ)
|
||||
expectedType = typ
|
||||
|
||||
var last = n.len - 1
|
||||
var catchAllExcepts = 0
|
||||
@@ -294,7 +303,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
if a.kind != nkFinally:
|
||||
a[^1] = semExprBranchScope(c, a[^1], expectedType)
|
||||
typ = commonType(c, typ, a[^1])
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(a[^1]):
|
||||
expectedType = typ
|
||||
else:
|
||||
a[^1] = semExprBranchScope(c, a[^1])
|
||||
dec last
|
||||
@@ -307,7 +317,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
result.typ = c.enforceVoidContext
|
||||
else:
|
||||
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
|
||||
n[0] = fitNode(c, typ, n[0], n[0].info)
|
||||
if not endsInNoReturn(n[0]):
|
||||
n[0] = fitNode(c, typ, n[0], n[0].info)
|
||||
for i in 1..last:
|
||||
var it = n[i]
|
||||
let j = it.len-1
|
||||
@@ -325,6 +336,8 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
|
||||
result.typ = typ
|
||||
if not floatRangeCheck(result.floatVal, typ):
|
||||
localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
|
||||
elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
|
||||
discard "keep nkHiddenStdConv for cstring conversions"
|
||||
else:
|
||||
changeType(c, r1, typ, check=true)
|
||||
result = r1
|
||||
@@ -392,7 +405,7 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
|
||||
result.add identDefs
|
||||
|
||||
proc isDiscardUnderscore(v: PSym): bool =
|
||||
if v.name.s == "_":
|
||||
if v.name.id == ord(wUnderscore):
|
||||
v.flags.incl(sfGenSym)
|
||||
result = true
|
||||
|
||||
@@ -459,7 +472,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
let y = considerQuotedIdent(c, n[1])
|
||||
let obj = x.typ.skipTypes(abstractPtrs)
|
||||
if obj.kind == tyObject and tfPartial in obj.flags:
|
||||
let field = newSym(skField, getIdent(c.cache, y.s), nextSymId c.idgen, obj.sym, n[1].info)
|
||||
let field = newSym(skField, getIdent(c.cache, y.s), c.idgen, obj.sym, n[1].info)
|
||||
field.typ = skipIntLit(typ, c.idgen)
|
||||
field.position = obj.n.len
|
||||
obj.n.add newSymNode(field)
|
||||
@@ -562,8 +575,9 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
|
||||
return result
|
||||
|
||||
template isLocalSym(sym: PSym): bool =
|
||||
sym.kind in {skVar, skLet} and not
|
||||
sym.kind in {skVar, skLet, skParam} and not
|
||||
({sfGlobal, sfPure} * sym.flags != {} or
|
||||
sym.typ.kind == tyTypeDesc or
|
||||
sfCompileTime in sym.flags) or
|
||||
sym.kind in {skProc, skFunc, skIterator} and
|
||||
sfGlobal notin sym.flags
|
||||
@@ -583,6 +597,66 @@ proc globalVarInitCheck(c: PContext, n: PNode) =
|
||||
if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
|
||||
localError(c.config, n.info, errCannotAssignToGlobal)
|
||||
|
||||
const
|
||||
errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'"
|
||||
errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements"
|
||||
|
||||
proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
|
||||
## expand tuple unpacking assignments into new var/let/const section
|
||||
##
|
||||
## mirrored with semexprs.makeTupleAssignments
|
||||
if typ.kind != tyTuple:
|
||||
localError(c.config, a.info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(typ, preferDesc)])
|
||||
elif a.len-2 != typ.len:
|
||||
localError(c.config, a.info, errTupleUnpackingDifferentLengths %
|
||||
[$(a.len-2), typeToString(typ, preferDesc), $typ.len])
|
||||
var
|
||||
tempNode: PNode = nil
|
||||
lastDef: PNode
|
||||
let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
|
||||
# temporary not needed if not const and RHS is tuple literal
|
||||
# const breaks with seqs without temporary
|
||||
let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
|
||||
if useTemp:
|
||||
# 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)
|
||||
lastDef = newNodeI(defkind, a.info)
|
||||
newSons(lastDef, 3)
|
||||
lastDef[0] = newSymNode(temp)
|
||||
# NOTE: at the moment this is always ast.emptyNode, see parser.nim
|
||||
lastDef[1] = a[^2]
|
||||
lastDef[2] = def
|
||||
temp.ast = lastDef
|
||||
addToVarSection(c, origResult, n, lastDef)
|
||||
tempNode = newSymNode(temp)
|
||||
result = newNodeI(n.kind, a.info)
|
||||
for j in 0..<a.len-2:
|
||||
let name = a[j]
|
||||
if useTemp and name.kind == nkIdent and name.ident.id == ord(wUnderscore):
|
||||
# skip _ assignments if we are using a temp as they are already evaluated
|
||||
continue
|
||||
if name.kind == nkVarTuple:
|
||||
# nested tuple
|
||||
lastDef = newNodeI(nkVarTuple, name.info)
|
||||
newSons(lastDef, name.len)
|
||||
for k in 0..<name.len-2:
|
||||
lastDef[k] = name[k]
|
||||
else:
|
||||
lastDef = newNodeI(defkind, name.info)
|
||||
newSons(lastDef, 3)
|
||||
lastDef[0] = name
|
||||
lastDef[^2] = c.graph.emptyNode
|
||||
if useTemp:
|
||||
lastDef[^1] = newTupleAccessRaw(tempNode, j)
|
||||
else:
|
||||
var val = def[j]
|
||||
if val.kind == nkExprColonExpr: val = val[1]
|
||||
lastDef[^1] = val
|
||||
result.add(lastDef)
|
||||
|
||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
var b: PNode
|
||||
result = copyNode(n)
|
||||
@@ -598,9 +672,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
addToVarSection(c, result, b)
|
||||
continue
|
||||
|
||||
var hasUserSpecifiedType = false
|
||||
var typ: PType = nil
|
||||
if a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
hasUserSpecifiedType = true
|
||||
|
||||
var typFlags: TTypeAllowedFlags
|
||||
|
||||
@@ -629,8 +705,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
if hasEmpty(typ):
|
||||
localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr)
|
||||
elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast):
|
||||
# tfUnresolved in typ.flags:
|
||||
localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
|
||||
let owner = typ.owner
|
||||
let err =
|
||||
# consistent error message with evaltempl/semMacroExpr
|
||||
if owner != nil and owner.kind in {skTemplate, skMacro}:
|
||||
errMissingGenericParamsForTemplate % def.renderTree
|
||||
else:
|
||||
errProcHasNoConcreteType % def.renderTree
|
||||
localError(c.config, def.info, err)
|
||||
when false:
|
||||
# XXX This typing rule is neither documented nor complete enough to
|
||||
# justify it. Instead use the newer 'unowned x' until we figured out
|
||||
@@ -649,43 +731,39 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
|
||||
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||
if a.kind == nkVarTuple:
|
||||
if tup.kind != tyTuple:
|
||||
localError(c.config, a.info, errXExpected, "tuple")
|
||||
elif a.len-2 != tup.len:
|
||||
localError(c.config, a.info, errWrongNumberOfVariables)
|
||||
b = newNodeI(nkVarTuple, a.info)
|
||||
newSons(b, a.len)
|
||||
# keep type desc for doc generator
|
||||
# NOTE: at the moment this is always ast.emptyNode, see parser.nim
|
||||
b[^2] = a[^2]
|
||||
b[^1] = def
|
||||
addToVarSection(c, result, n, b)
|
||||
elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
|
||||
a.kind == nkIdentDefs and a.len > 3:
|
||||
message(c.config, a.info, warnEachIdentIsTuple)
|
||||
# generate new section from tuple unpacking and embed it into this one
|
||||
let assignments = makeVarTupleSection(c, n, a, def, tup, symkind, result)
|
||||
let resSection = semVarOrLet(c, assignments, symkind)
|
||||
for resDef in resSection:
|
||||
addToVarSection(c, result, n, resDef)
|
||||
else:
|
||||
if tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
|
||||
a.len > 3:
|
||||
# var a, b = (1, 2)
|
||||
message(c.config, a.info, warnEachIdentIsTuple)
|
||||
|
||||
for j in 0..<a.len-2:
|
||||
if a[j].kind == nkDotExpr:
|
||||
fillPartialObject(c, a[j],
|
||||
if a.kind != nkVarTuple: typ else: tup[j])
|
||||
addToVarSection(c, result, n, a)
|
||||
continue
|
||||
var v = semIdentDef(c, a[j], symkind, false)
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
if sfGenSym notin v.flags:
|
||||
if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
|
||||
else:
|
||||
if v.owner == nil: v.owner = c.p.owner
|
||||
when oKeepVariableNames:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
for j in 0..<a.len-2:
|
||||
if a[j].kind == nkDotExpr:
|
||||
fillPartialObject(c, a[j], typ)
|
||||
addToVarSection(c, result, n, a)
|
||||
continue
|
||||
var v = semIdentDef(c, a[j], symkind, false)
|
||||
when defined(nimsuggest):
|
||||
v.hasUserSpecifiedType = hasUserSpecifiedType
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
if sfGenSym notin v.flags:
|
||||
if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
|
||||
else:
|
||||
let shadowed = findShadowedVar(c, v)
|
||||
if shadowed != nil:
|
||||
shadowed.flags.incl(sfShadowed)
|
||||
if shadowed.kind == skResult and sfGenSym notin v.flags:
|
||||
message(c.config, a.info, warnResultShadowed)
|
||||
if a.kind != nkVarTuple:
|
||||
if v.owner == nil: v.owner = c.p.owner
|
||||
when oKeepVariableNames:
|
||||
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
|
||||
else:
|
||||
let shadowed = findShadowedVar(c, v)
|
||||
if shadowed != nil:
|
||||
shadowed.flags.incl(sfShadowed)
|
||||
if shadowed.kind == skResult and sfGenSym notin v.flags:
|
||||
message(c.config, a.info, warnResultShadowed)
|
||||
if def.kind != nkEmpty:
|
||||
if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
|
||||
setVarType(c, v, typ)
|
||||
@@ -708,35 +786,26 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
b.add copyTree(def)
|
||||
addToVarSection(c, result, n, b)
|
||||
v.ast = b
|
||||
else:
|
||||
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
|
||||
# bug #7663, for 'nim check' this can be a non-tuple:
|
||||
if tup.kind == tyTuple: setVarType(c, v, tup[j])
|
||||
else: v.typ = tup
|
||||
b[j] = newSymNode(v)
|
||||
if def.kind == nkEmpty:
|
||||
let actualType = v.typ.skipTypes({tyGenericInst, tyAlias,
|
||||
tyUserTypeClassInst})
|
||||
if actualType.kind in {tyObject, tyDistinct} and
|
||||
actualType.requiresInit:
|
||||
defaultConstructionError(c, v.typ, v.info)
|
||||
else:
|
||||
checkNilable(c, v)
|
||||
# allow let to not be initialised if imported from C:
|
||||
if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)):
|
||||
localError(c.config, a.info, errLetNeedsInit)
|
||||
if sfCompileTime in v.flags:
|
||||
if a.kind != nkVarTuple:
|
||||
if def.kind == nkEmpty:
|
||||
let actualType = v.typ.skipTypes({tyGenericInst, tyAlias,
|
||||
tyUserTypeClassInst})
|
||||
if actualType.kind in {tyObject, tyDistinct} and
|
||||
actualType.requiresInit:
|
||||
defaultConstructionError(c, v.typ, v.info)
|
||||
else:
|
||||
checkNilable(c, v)
|
||||
# allow let to not be initialised if imported from C:
|
||||
if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)):
|
||||
localError(c.config, a.info, errLetNeedsInit)
|
||||
if sfCompileTime in v.flags:
|
||||
var x = newNodeI(result.kind, v.info)
|
||||
x.add result[i]
|
||||
vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x)
|
||||
else:
|
||||
localError(c.config, a.info, "cannot destructure to compile time variable")
|
||||
if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
|
||||
message(c.config, v.info, hintGlobalVar)
|
||||
if {sfGlobal, sfPure} <= v.flags:
|
||||
globalVarInitCheck(c, def)
|
||||
suggestSym(c.graph, v.info, v, c.graph.usageSym)
|
||||
if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
|
||||
message(c.config, v.info, hintGlobalVar)
|
||||
if {sfGlobal, sfPure} <= v.flags:
|
||||
globalVarInitCheck(c, def)
|
||||
suggestSym(c.graph, v.info, v, c.graph.usageSym)
|
||||
|
||||
proc semConst(c: PContext, n: PNode): PNode =
|
||||
result = copyNode(n)
|
||||
@@ -754,9 +823,11 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
addToVarSection(c, result, b)
|
||||
continue
|
||||
|
||||
var hasUserSpecifiedType = false
|
||||
var typ: PType = nil
|
||||
if a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
hasUserSpecifiedType = true
|
||||
|
||||
var typFlags: TTypeAllowedFlags
|
||||
|
||||
@@ -789,26 +860,28 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
typeAllowedCheck(c, a.info, typ, skConst, typFlags)
|
||||
|
||||
if a.kind == nkVarTuple:
|
||||
if typ.kind != tyTuple:
|
||||
localError(c.config, a.info, errXExpected, "tuple")
|
||||
elif a.len-2 != typ.len:
|
||||
localError(c.config, a.info, errWrongNumberOfVariables)
|
||||
b = newNodeI(nkVarTuple, a.info)
|
||||
newSons(b, a.len)
|
||||
b[^2] = a[^2]
|
||||
b[^1] = def
|
||||
# generate new section from tuple unpacking and embed it into this one
|
||||
let assignments = makeVarTupleSection(c, n, a, def, typ, skConst, result)
|
||||
let resSection = semConst(c, assignments)
|
||||
for resDef in resSection:
|
||||
addToVarSection(c, result, n, resDef)
|
||||
else:
|
||||
for j in 0..<a.len-2:
|
||||
var v = semIdentDef(c, a[j], skConst)
|
||||
when defined(nimsuggest):
|
||||
v.hasUserSpecifiedType = hasUserSpecifiedType
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
|
||||
for j in 0..<a.len-2:
|
||||
var v = semIdentDef(c, a[j], skConst)
|
||||
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
|
||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||
styleCheckDef(c, v)
|
||||
onDef(a[j].info, v)
|
||||
|
||||
if a.kind != nkVarTuple:
|
||||
setVarType(c, v, typ)
|
||||
when false:
|
||||
v.ast = def # no need to copy
|
||||
var fillSymbol = true
|
||||
if v.typ != nil:
|
||||
# symbol already has type and probably value
|
||||
# don't mutate
|
||||
fillSymbol = false
|
||||
else:
|
||||
setVarType(c, v, typ)
|
||||
b = newNodeI(nkConstDef, a.info)
|
||||
if importantComments(c.config): b.comment = a.comment
|
||||
# postfix not generated here (to generate, get rid of it in transf)
|
||||
@@ -821,13 +894,9 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
b.add newSymNode(v)
|
||||
b.add a[1]
|
||||
b.add copyTree(def)
|
||||
v.ast = b
|
||||
else:
|
||||
setVarType(c, v, typ[j])
|
||||
v.ast = if def[j].kind != nkExprColonExpr: def[j]
|
||||
else: def[j][1]
|
||||
b[j] = newSymNode(v)
|
||||
addToVarSection(c, result, n, b)
|
||||
if fillSymbol:
|
||||
v.ast = b
|
||||
addToVarSection(c, result, n, b)
|
||||
dec c.inStaticContext
|
||||
|
||||
include semfields
|
||||
@@ -854,7 +923,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if iterAfterVarLent.kind != tyTuple or n.len == 3:
|
||||
if n.len == 3:
|
||||
if n[0].kind == nkVarTuple:
|
||||
if n[0].len-1 != iterAfterVarLent.len:
|
||||
if iterAfterVarLent.kind != tyTuple:
|
||||
return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected %
|
||||
[typeToString(n[1].typ, preferDesc)])
|
||||
elif n[0].len-1 != iterAfterVarLent.len:
|
||||
return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables)
|
||||
|
||||
for i in 0..<n[0].len-1:
|
||||
@@ -1034,7 +1106,8 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
result = n
|
||||
n[^2] = semExprNoDeref(c, n[^2], {efWantIterator})
|
||||
var call = n[^2]
|
||||
if call.kind == nkStmtListExpr and isTrivalStmtExpr(call):
|
||||
|
||||
if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)):
|
||||
call = call.lastSon
|
||||
n[^2] = call
|
||||
let isCallExpr = call.kind in nkCallKinds
|
||||
@@ -1072,29 +1145,23 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
|
||||
openScope(c)
|
||||
pushCaseContext(c, n)
|
||||
n[0] = semExprWithType(c, n[0])
|
||||
var chckCovered = false
|
||||
var covered: Int128 = toInt128(0)
|
||||
var typ = commonTypeBegin
|
||||
var expectedType = expectedType
|
||||
var hasElse = false
|
||||
let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc})
|
||||
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
|
||||
var chckCovered = caseTyp.shouldCheckCaseCovered()
|
||||
case caseTyp.kind
|
||||
of shouldChckCovered:
|
||||
chckCovered = true
|
||||
of tyRange:
|
||||
if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyString, tyCstring, tyError:
|
||||
of tyFloat..tyFloat128, tyString, tyCstring, tyError, shouldChckCovered, tyRange:
|
||||
discard
|
||||
else:
|
||||
popCaseContext(c)
|
||||
closeScope(c)
|
||||
return handleCaseStmtMacro(c, n, flags)
|
||||
template invalidOrderOfBranches(n: PNode) =
|
||||
template invalidOrderOfBranches(n: PNode) =
|
||||
localError(c.config, n.info, "invalid order of case branches")
|
||||
break
|
||||
|
||||
|
||||
for i in 1..<n.len:
|
||||
setCaseContextIdx(c, i)
|
||||
var x = n[i]
|
||||
@@ -1109,7 +1176,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
|
||||
var last = x.len-1
|
||||
x[last] = semExprBranchScope(c, x[last], expectedType)
|
||||
typ = commonType(c, typ, x[last])
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(x[last]):
|
||||
expectedType = typ
|
||||
of nkElifBranch:
|
||||
if hasElse: invalidOrderOfBranches(x)
|
||||
chckCovered = false
|
||||
@@ -1118,13 +1186,15 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
|
||||
x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool)))
|
||||
x[1] = semExprBranch(c, x[1], expectedType = expectedType)
|
||||
typ = commonType(c, typ, x[1])
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(x[1]):
|
||||
expectedType = typ
|
||||
closeScope(c)
|
||||
of nkElse:
|
||||
checkSonsLen(x, 1, c.config)
|
||||
x[0] = semExprBranchScope(c, x[0], expectedType)
|
||||
typ = commonType(c, typ, x[0])
|
||||
expectedType = typ
|
||||
if not endsInNoReturn(x[0]):
|
||||
expectedType = typ
|
||||
if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse:
|
||||
message(c.config, x.info, warnUnreachableElse)
|
||||
hasElse = true
|
||||
@@ -1417,12 +1487,20 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
# final pass
|
||||
if a[2].kind in nkCallKinds:
|
||||
incl a[2].flags, nfSem # bug #10548
|
||||
if sfExportc in s.flags and s.typ.kind == tyAlias:
|
||||
localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
|
||||
|
||||
if tfBorrowDot in s.typ.flags and s.typ.skipTypes({tyGenericBody}).kind != tyDistinct:
|
||||
excl s.typ.flags, tfBorrowDot
|
||||
localError(c.config, name.info, "only a 'distinct' type can borrow `.`")
|
||||
if sfExportc in s.flags:
|
||||
if s.typ.kind == tyAlias:
|
||||
localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
|
||||
elif s.typ.kind == tyGenericBody:
|
||||
localError(c.config, name.info, "{.exportc.} not allowed for generic types")
|
||||
|
||||
if tfBorrowDot in s.typ.flags:
|
||||
let body = s.typ.skipTypes({tyGenericBody})
|
||||
if body.kind != tyDistinct:
|
||||
# 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
|
||||
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:
|
||||
@@ -1440,7 +1518,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
if tfInheritable in oldFlags and tfFinal in objTy.flags:
|
||||
excl(objTy.flags, tfFinal)
|
||||
let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
|
||||
nextSymId c.idgen, getCurrOwner(c), s.info)
|
||||
c.idgen, getCurrOwner(c), s.info)
|
||||
obj.flags.incl sfGeneratedType
|
||||
let symNode = newSymNode(obj)
|
||||
obj.ast = a.shallowCopy
|
||||
@@ -1457,22 +1535,30 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
obj.flags.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) =
|
||||
proc checkMeta(c: PContext; n: PNode; t: PType) =
|
||||
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
|
||||
proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
||||
proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) =
|
||||
if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags:
|
||||
if t.kind == tyBuiltInTypeClass and t.len == 1 and t[0].kind == tyProc:
|
||||
localError(c.config, n.info, ("'$1' is not a concrete type; " &
|
||||
"for a callback without parameters use 'proc()'") % t.typeToString)
|
||||
elif t.kind == tyNone and parent != nil:
|
||||
# TODO: openarray has the `tfGenericTypeParam` flag & generics
|
||||
# TODO: handle special cases (sink etc.) and views
|
||||
localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString)
|
||||
else:
|
||||
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
|
||||
hasError = true
|
||||
|
||||
if n.isNil: return
|
||||
case n.kind
|
||||
of nkRecList, nkRecCase:
|
||||
for s in n: checkForMetaFields(c, s)
|
||||
for s in n: checkForMetaFields(c, s, hasError)
|
||||
of nkOfBranch, nkElse:
|
||||
checkForMetaFields(c, n.lastSon)
|
||||
checkForMetaFields(c, n.lastSon, hasError)
|
||||
of nkSym:
|
||||
let t = n.sym.typ
|
||||
case t.kind
|
||||
@@ -1480,9 +1566,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
|
||||
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned:
|
||||
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
|
||||
for i in start..<t.len:
|
||||
checkMeta(c, n, t[i])
|
||||
checkMeta(c, n, t[i], hasError, t)
|
||||
else:
|
||||
checkMeta(c, n, t)
|
||||
checkMeta(c, n, t, hasError, nil)
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
@@ -1517,14 +1603,15 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
assert s.typ != nil
|
||||
assignType(s.typ, t)
|
||||
s.typ.itemId = t.itemId # same id
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
var hasError = false
|
||||
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
|
||||
checkForMetaFields(c, s.typ.n)
|
||||
checkForMetaFields(c, s.typ.n, hasError)
|
||||
if not hasError:
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
|
||||
# fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
|
||||
if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
|
||||
incl(s.flags, sfGenSym)
|
||||
|
||||
#instAllTypeBoundOp(c, n.info)
|
||||
|
||||
|
||||
@@ -1598,18 +1685,27 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) =
|
||||
|
||||
proc semBorrow(c: PContext, n: PNode, s: PSym) =
|
||||
# search for the correct alias:
|
||||
var b = searchForBorrowProc(c, c.currentScope.parent, s)
|
||||
if b != nil:
|
||||
# store the alias:
|
||||
n[bodyPos] = newSymNode(b)
|
||||
# Carry over the original symbol magic, this is necessary in order to ensure
|
||||
# the semantic pass is correct
|
||||
s.magic = b.magic
|
||||
if b.typ != nil and b.typ.len > 0:
|
||||
s.typ.n[0] = b.typ.n[0]
|
||||
s.typ.flags = b.typ.flags
|
||||
else:
|
||||
localError(c.config, n.info, errNoSymbolToBorrowFromFound)
|
||||
var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s)
|
||||
case state
|
||||
of bsMatch:
|
||||
# store the alias:
|
||||
n[bodyPos] = newSymNode(b)
|
||||
# Carry over the original symbol magic, this is necessary in order to ensure
|
||||
# the semantic pass is correct
|
||||
s.magic = b.magic
|
||||
if b.typ != nil and b.typ.len > 0:
|
||||
s.typ.n[0] = b.typ.n[0]
|
||||
s.typ.flags = b.typ.flags
|
||||
of bsNoDistinct:
|
||||
localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless")
|
||||
of bsReturnNotMatch:
|
||||
localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ[0]))
|
||||
of bsGeneric:
|
||||
localError(c.config, n.info, "borrow with generic parameter is not supported")
|
||||
of bsNotSupported:
|
||||
localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s)
|
||||
else:
|
||||
localError(c.config, n.info, errNoSymbolToBorrowFromFound)
|
||||
|
||||
proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
|
||||
## Swap nodes that are (skResult) symbols to d(estination)Node.
|
||||
@@ -1618,9 +1714,19 @@ proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
|
||||
n[i] = dNode
|
||||
swapResult(n[i], sRes, dNode)
|
||||
|
||||
|
||||
proc addThis(c: PContext, n: PNode, t: PType, owner: TSymKind) =
|
||||
var s = newSym(skResult, getIdent(c.cache, "this"), c.idgen,
|
||||
getCurrOwner(c), n.info)
|
||||
s.typ = t
|
||||
incl(s.flags, sfUsed)
|
||||
c.p.resultSym = s
|
||||
n.add newSymNode(c.p.resultSym)
|
||||
addParamOrResult(c, c.p.resultSym, owner)
|
||||
|
||||
proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
|
||||
template genResSym(s) =
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId c.idgen,
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen,
|
||||
getCurrOwner(c), n.info)
|
||||
s.typ = t
|
||||
incl(s.flags, sfUsed)
|
||||
@@ -1707,7 +1813,6 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||
n[genericParamsPos] = c.graph.emptyNode
|
||||
# for LL we need to avoid wrong aliasing
|
||||
let params = copyTree n.typ.n
|
||||
n[paramsPos] = params
|
||||
s.typ = n.typ
|
||||
for i in 1..<params.len:
|
||||
if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
|
||||
@@ -1761,7 +1866,7 @@ proc canonType(c: PContext, t: PType): PType =
|
||||
|
||||
proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
|
||||
var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
|
||||
if sfOverriden notin prevOp.flags:
|
||||
if sfOverridden notin prevOp.flags:
|
||||
msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
|
||||
else:
|
||||
msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
|
||||
@@ -1776,13 +1881,59 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType =
|
||||
if result.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
result = canonType(c, result)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = if op in {attachedDestructor, attachedWasMoved}:
|
||||
let cond = t.len == 2 and t[0] != nil
|
||||
|
||||
if cond:
|
||||
var obj = t[1]
|
||||
while true:
|
||||
incl(obj.flags, tfHasAsgn)
|
||||
if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
|
||||
elif obj.kind == tyGenericInvocation: obj = obj[0]
|
||||
else: break
|
||||
|
||||
var res = t[0]
|
||||
while true:
|
||||
if res.kind in {tyGenericBody, tyGenericInst}: res = res.lastSon
|
||||
elif res.kind == tyGenericInvocation: res = res[0]
|
||||
else: break
|
||||
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res):
|
||||
obj = canonType(c, obj)
|
||||
let ao = getAttachedOp(c.graph, obj, op)
|
||||
if ao == s:
|
||||
discard "forward declared destructor"
|
||||
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
|
||||
setAttachedOp(c.graph, c.module.position, obj, op, s)
|
||||
else:
|
||||
prevDestructor(c, ao, obj, n.info)
|
||||
noError = true
|
||||
if obj.owner.getModule != s.getModule:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
|
||||
if not noError and sfSystemModule notin s.owner.flags:
|
||||
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)
|
||||
|
||||
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
|
||||
let t = s.typ
|
||||
var noError = false
|
||||
let cond = case op
|
||||
of attachedWasMoved:
|
||||
t.len == 2 and t[0] == nil and t[1].kind == tyVar
|
||||
elif op == attachedTrace:
|
||||
of attachedTrace:
|
||||
t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer
|
||||
of attachedDestructor:
|
||||
if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
t.len == 2 and t[0] == nil
|
||||
else:
|
||||
t.len == 2 and t[0] == nil and t[1].kind == tyVar
|
||||
else:
|
||||
t.len >= 2 and t[0] == nil
|
||||
|
||||
@@ -1794,6 +1945,9 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
elif obj.kind == tyGenericInvocation: obj = obj[0]
|
||||
else: break
|
||||
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
|
||||
if (not suppressVarDestructorWarning) and op == attachedDestructor and t[1].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:
|
||||
@@ -1807,20 +1961,33 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
|
||||
if not noError and sfSystemModule notin s.owner.flags:
|
||||
if op == attachedTrace:
|
||||
case op
|
||||
of attachedTrace:
|
||||
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}:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
|
||||
else:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"signature for '=destroy' must be proc[T: object](x: var T)")
|
||||
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, sfOverriden)
|
||||
incl(s.flags, sfOverridden)
|
||||
|
||||
proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
let name = s.name.s.normalize
|
||||
case name
|
||||
of "=destroy":
|
||||
bindTypeHook(c, s, n, attachedDestructor)
|
||||
if s.ast != nil:
|
||||
if s.ast[pragmasPos].kind == nkEmpty:
|
||||
s.ast[pragmasPos] = newNodeI(nkPragma, s.info)
|
||||
s.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(c.cache.getIdent("raises"), s.info), newNodeI(nkBracket, s.info))
|
||||
of "deepcopy", "=deepcopy":
|
||||
if s.typ.len == 2 and
|
||||
s.typ[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
|
||||
@@ -1850,11 +2017,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
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, sfOverriden)
|
||||
incl(s.flags, sfOverridden)
|
||||
of "=", "=copy", "=sink":
|
||||
if s.magic == mAsgn: return
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverriden)
|
||||
incl(s.flags, sfOverridden)
|
||||
if name == "=":
|
||||
message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead")
|
||||
let t = s.typ
|
||||
@@ -1897,8 +2064,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
of "=wasmoved":
|
||||
if s.magic != mWasMoved:
|
||||
bindTypeHook(c, s, n, attachedWasMoved)
|
||||
of "=dup":
|
||||
if s.magic != mDup:
|
||||
bindDupHook(c, s, n, attachedDup)
|
||||
else:
|
||||
if sfOverriden in s.flags:
|
||||
if sfOverridden in s.flags:
|
||||
localError(c.config, n.info, errGenerated,
|
||||
"'destroy' or 'deepCopy' expected for 'override'")
|
||||
|
||||
@@ -1961,7 +2131,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
|
||||
case n[namePos].kind
|
||||
of nkEmpty:
|
||||
s = newSym(kind, c.cache.idAnon, nextSymId c.idgen, c.getCurrOwner, n.info)
|
||||
s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
|
||||
s.flags.incl sfUsed
|
||||
n[namePos] = newSymNode(s)
|
||||
of nkSym:
|
||||
@@ -1983,6 +2153,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
s.ast = n
|
||||
s.options = c.config.options
|
||||
#s.scope = c.currentScope
|
||||
if s.kind in {skMacro, skTemplate}:
|
||||
# push noalias flag at first to prevent unwanted recursive calls:
|
||||
incl(s.flags, sfNoalias)
|
||||
|
||||
# before compiling the proc params & body, set as current the scope
|
||||
# where the proc was declared
|
||||
@@ -2123,7 +2296,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
pushOwner(c, s)
|
||||
|
||||
if not isAnon:
|
||||
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
||||
if sfOverridden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
|
||||
elif s.name.s[0] in {'.', '('}:
|
||||
if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
|
||||
localError(c.config, n.info, "the overloaded " & s.name.s &
|
||||
@@ -2135,6 +2308,34 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
if sfBorrow in s.flags and c.config.cmd notin cmdDocLike:
|
||||
result[bodyPos] = c.graph.emptyNode
|
||||
|
||||
if {sfVirtual, sfConstructor} * s.flags != {} and sfImportc notin s.flags:
|
||||
let isVirtual = sfVirtual in s.flags
|
||||
let pragmaName = if isVirtual: "virtual" else: "constructor"
|
||||
if c.config.backend == backendCpp:
|
||||
if s.typ.sons.len < 2 and isVirtual:
|
||||
localError(c.config, n.info, "virtual must have at least one parameter")
|
||||
for son in s.typ.sons:
|
||||
if son!=nil and son.isMetaType:
|
||||
localError(c.config, n.info, pragmaName & " unsupported for generic routine")
|
||||
var typ: PType
|
||||
if sfConstructor in s.flags:
|
||||
typ = s.typ.sons[0]
|
||||
if typ == nil or typ.kind != tyObject:
|
||||
localError(c.config, n.info, "constructor must return an object")
|
||||
else:
|
||||
typ = s.typ.sons[1]
|
||||
if typ.kind == tyPtr and isVirtual:
|
||||
typ = typ[0]
|
||||
if typ.kind != tyObject:
|
||||
localError(c.config, n.info, "virtual must be either ptr to object or object type.")
|
||||
if typ.owner.id == s.owner.id and c.module.id == s.owner.id:
|
||||
c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s
|
||||
else:
|
||||
localError(c.config, n.info,
|
||||
pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope")
|
||||
else:
|
||||
localError(c.config, n.info, pragmaName & " procs are only supported in C++")
|
||||
|
||||
if n[bodyPos].kind != nkEmpty and sfError notin s.flags:
|
||||
# for DLL generation we allow sfImportc to have a body, for use in VM
|
||||
if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not
|
||||
@@ -2159,15 +2360,19 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
# Macros and Templates can have generic parameters, but they are only
|
||||
# used for overload resolution (there is no instantiation of the symbol)
|
||||
if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
|
||||
|
||||
maybeAddResult(c, s, n)
|
||||
let resultType =
|
||||
if s.kind == skMacro:
|
||||
sysTypeFromName(c.graph, n.info, "NimNode")
|
||||
elif not isInlineIterator(s.typ):
|
||||
s.typ[0]
|
||||
else:
|
||||
nil
|
||||
var resultType: PType
|
||||
if sfConstructor in s.flags:
|
||||
resultType = makePtrType(c, s.typ[0])
|
||||
addThis(c, n, resultType, skProc)
|
||||
else:
|
||||
maybeAddResult(c, s, n)
|
||||
resultType =
|
||||
if s.kind == skMacro:
|
||||
sysTypeFromName(c.graph, n.info, "NimNode")
|
||||
elif not isInlineIterator(s.typ):
|
||||
s.typ[0]
|
||||
else:
|
||||
nil
|
||||
# semantic checking also needed with importc in case used in VM
|
||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType))
|
||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||
@@ -2175,7 +2380,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
trackProc(c, s, s.ast[bodyPos])
|
||||
else:
|
||||
if (s.typ[0] != nil and s.kind != skIterator):
|
||||
addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, s, n.info))
|
||||
addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, s, n.info))
|
||||
|
||||
openScope(c)
|
||||
n[bodyPos] = semGenericStmt(c, n[bodyPos])
|
||||
@@ -2275,7 +2480,6 @@ proc semMethod(c: PContext, n: PNode): PNode =
|
||||
|
||||
proc semConverterDef(c: PContext, n: PNode): PNode =
|
||||
if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "converter")
|
||||
checkSonsLen(n, bodyPos + 1, c.config)
|
||||
result = semProcAux(c, n, skConverter, converterPragmas)
|
||||
# macros can transform converters to nothing:
|
||||
if namePos >= result.safeLen: return result
|
||||
@@ -2290,7 +2494,6 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
|
||||
addConverterDef(c, LazySym(sym: s))
|
||||
|
||||
proc semMacroDef(c: PContext, n: PNode): PNode =
|
||||
checkSonsLen(n, bodyPos + 1, c.config)
|
||||
result = semProcAux(c, n, skMacro, macroPragmas)
|
||||
# macros can transform macros to nothing:
|
||||
if namePos >= result.safeLen: return result
|
||||
@@ -2301,10 +2504,16 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
|
||||
var s = result[namePos].sym
|
||||
var t = s.typ
|
||||
var allUntyped = true
|
||||
var nullary = true
|
||||
for i in 1..<t.n.len:
|
||||
let param = t.n[i].sym
|
||||
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 nullary and n[genericParamsPos].kind == nkEmpty:
|
||||
# macro can be called with alias syntax, remove pushed noalias flag
|
||||
excl(s.flags, sfNoalias)
|
||||
if n[bodyPos].kind == nkEmpty:
|
||||
localError(c.config, n.info, errImplOfXexpected % s.name.s)
|
||||
|
||||
@@ -2459,9 +2668,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
|
||||
var m = n[i]
|
||||
while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
|
||||
m = m.lastSon
|
||||
if m.kind in nkLastBlockStmts or
|
||||
m.kind in nkCallKinds and m[0].kind == nkSym and
|
||||
sfNoReturn in m[0].sym.flags:
|
||||
if endsInNoReturn(m):
|
||||
for j in i + 1..<n.len:
|
||||
case n[j].kind
|
||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard
|
||||
@@ -2477,12 +2684,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
|
||||
result[0].kind != nkDefer:
|
||||
result = result[0]
|
||||
|
||||
when defined(nimfix):
|
||||
if result.kind == nkCommentStmt and not result.comment.isNil and
|
||||
not (result.comment[0] == '#' and result.comment[1] == '#'):
|
||||
# it is an old-style comment statement: we replace it with 'discard ""':
|
||||
prettybase.replaceComment(result.info)
|
||||
|
||||
proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if efInTypeof notin flags:
|
||||
result = semExprNoType(c, n)
|
||||
|
||||
@@ -70,6 +70,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
onUse(info, s)
|
||||
else:
|
||||
result = n
|
||||
elif i == 0:
|
||||
# forced open but symbol not in scope, retain information
|
||||
result = n
|
||||
else:
|
||||
# semantic checking requires a type; ``fitNode`` deals with it
|
||||
# appropriately
|
||||
@@ -110,14 +113,10 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
|
||||
|
||||
proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
|
||||
result = copyNode(n)
|
||||
var count = 0
|
||||
for i in 0..<n.len:
|
||||
toMixin.incl(considerQuotedIdent(c, n[i]).id)
|
||||
let x = symChoice(c, n[i], nil, scForceOpen)
|
||||
inc count, x.len
|
||||
result.add x
|
||||
if count == 0:
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
|
||||
proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
|
||||
case n.kind
|
||||
@@ -136,24 +135,32 @@ type
|
||||
noGenSym: int
|
||||
inTemplateHeader: int
|
||||
|
||||
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
|
||||
proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} =
|
||||
result = s.kind == skParam and
|
||||
s.owner == c.owner and sfTemplateParam in s.flags
|
||||
|
||||
proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] =
|
||||
case n.kind
|
||||
of nkPostfix: result = getIdentNode(c, n[1])
|
||||
of nkPragmaExpr: result = getIdentNode(c, n[0])
|
||||
of nkPostfix: result = getIdentReplaceParams(c, n[1])
|
||||
of nkPragmaExpr: result = getIdentReplaceParams(c, n[0])
|
||||
of nkIdent:
|
||||
result = n
|
||||
result = (n, false)
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
result = newSymNode(s, n.info)
|
||||
of nkAccQuoted, nkSym: result = n
|
||||
if s != nil and isTemplParam(c, s):
|
||||
n = newSymNode(s, n.info)
|
||||
result = (n, true)
|
||||
of nkSym:
|
||||
result = (n, isTemplParam(c, n.sym))
|
||||
of nkAccQuoted:
|
||||
result = (n, false)
|
||||
for i in 0..<n.safeLen:
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n[i])
|
||||
if hasParam:
|
||||
result.node[i] = ident
|
||||
result.hasParam = true
|
||||
else:
|
||||
illFormedAst(n, c.c.config)
|
||||
result = n
|
||||
|
||||
proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
|
||||
result = n.kind == nkSym and n.sym.kind == skParam and
|
||||
n.sym.owner == c.owner and sfTemplateParam in n.sym.flags
|
||||
result = (n, false)
|
||||
|
||||
proc semTemplBody(c: var TemplCtx, n: PNode): PNode
|
||||
|
||||
@@ -168,47 +175,21 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
|
||||
result = semTemplBody(c, n)
|
||||
closeScope(c)
|
||||
|
||||
proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
|
||||
result = n
|
||||
if n.kind == nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
result[i] = onlyReplaceParams(c, n[i])
|
||||
|
||||
proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
|
||||
result = newSym(kind, considerQuotedIdent(c.c, n), nextSymId c.c.idgen, c.owner, n.info)
|
||||
result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info)
|
||||
incl(result.flags, sfGenSym)
|
||||
incl(result.flags, sfShadowed)
|
||||
|
||||
proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
# locals default to 'gensym':
|
||||
if n.kind == nkPragmaExpr and symBinding(n[1]) == spInject:
|
||||
# locals default to 'gensym', fields default to 'inject':
|
||||
if (n.kind == nkPragmaExpr and symBinding(n[1]) == spInject) or
|
||||
k == skField:
|
||||
# even if injected, don't produce a sym choice here:
|
||||
#n = semTemplBody(c, n)
|
||||
var x = n[0]
|
||||
while true:
|
||||
case x.kind
|
||||
of nkPostfix: x = x[1]
|
||||
of nkPragmaExpr: x = x[0]
|
||||
of nkIdent: break
|
||||
of nkAccQuoted:
|
||||
# consider: type `T TemplParam` {.inject.}
|
||||
# it suffices to return to treat it like 'inject':
|
||||
n = onlyReplaceParams(c, n)
|
||||
return
|
||||
else:
|
||||
illFormedAst(x, c.c.config)
|
||||
let ident = getIdentNode(c, x)
|
||||
if not isTemplParam(c, ident):
|
||||
c.toInject.incl(x.ident.id)
|
||||
else:
|
||||
replaceIdentBySym(c.c, n, ident)
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n)
|
||||
if not hasParam:
|
||||
if k != skField:
|
||||
c.toInject.incl(considerQuotedIdent(c.c, ident).id)
|
||||
else:
|
||||
if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
|
||||
let pragmaNode = n[1]
|
||||
@@ -218,16 +199,16 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
var found = false
|
||||
if ni.kind == nkIdent:
|
||||
for a in templatePragmas:
|
||||
if ni.ident == getIdent(c.c.cache, $a):
|
||||
if ni.ident.id == ord(a):
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
openScope(c)
|
||||
pragmaNode[i] = semTemplBody(c, pragmaNode[i])
|
||||
closeScope(c)
|
||||
let ident = getIdentNode(c, n)
|
||||
if not isTemplParam(c, ident):
|
||||
if n.kind != nkSym and not (n.kind == nkIdent and n.ident.s == "_"):
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n)
|
||||
if not hasParam:
|
||||
if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)):
|
||||
let local = newGenSym(k, ident, c)
|
||||
addPrelimDecl(c.c, local)
|
||||
styleCheckDef(c.c, n.info, local)
|
||||
@@ -235,8 +216,6 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||
replaceIdentBySym(c.c, n, newSymNode(local, n.info))
|
||||
if k == skParam and c.inTemplateHeader > 0:
|
||||
local.flags.incl sfTemplateParam
|
||||
else:
|
||||
replaceIdentBySym(c.c, n, ident)
|
||||
|
||||
proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
|
||||
incl(s.flags, sfUsed)
|
||||
@@ -281,7 +260,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
|
||||
if n.kind == nkIdent:
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags):
|
||||
if s.owner == c.owner and s.kind == skParam:
|
||||
incl(s.flags, sfUsed)
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
@@ -292,20 +271,21 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
|
||||
proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
||||
result = n
|
||||
checkSonsLen(n, bodyPos + 1, c.c.config)
|
||||
# routines default to 'inject':
|
||||
if n.kind notin nkLambdaKinds and symBinding(n[pragmasPos]) == spGenSym:
|
||||
let ident = getIdentNode(c, n[namePos])
|
||||
if not isTemplParam(c, ident):
|
||||
var s = newGenSym(k, ident, c)
|
||||
s.ast = n
|
||||
addPrelimDecl(c.c, s)
|
||||
styleCheckDef(c.c, n.info, s)
|
||||
onDef(n.info, s)
|
||||
n[namePos] = newSymNode(s, n[namePos].info)
|
||||
if n.kind notin nkLambdaKinds:
|
||||
# routines default to 'inject':
|
||||
if symBinding(n[pragmasPos]) == spGenSym:
|
||||
let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
|
||||
if not hasParam:
|
||||
var s = newGenSym(k, ident, c)
|
||||
s.ast = n
|
||||
addPrelimDecl(c.c, s)
|
||||
styleCheckDef(c.c, n.info, s)
|
||||
onDef(n.info, s)
|
||||
n[namePos] = newSymNode(s, n[namePos].info)
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = ident
|
||||
else:
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos])
|
||||
n[namePos] = semRoutineInTemplName(c, n[namePos])
|
||||
# open scope for parameters
|
||||
openScope(c)
|
||||
for i in patternPos..paramsPos-1:
|
||||
@@ -327,21 +307,24 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
|
||||
# close scope for parameters
|
||||
closeScope(c)
|
||||
|
||||
proc semTemplIdentDef(c: var TemplCtx, a: PNode, symKind: TSymKind) =
|
||||
checkMinSonsLen(a, 3, c.c.config)
|
||||
when defined(nimsuggest):
|
||||
inc c.c.inTypeContext
|
||||
a[^2] = semTemplBody(c, a[^2])
|
||||
when defined(nimsuggest):
|
||||
dec c.c.inTypeContext
|
||||
a[^1] = semTemplBody(c, a[^1])
|
||||
for j in 0..<a.len-2:
|
||||
addLocalDecl(c, a[j], symKind)
|
||||
|
||||
proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) =
|
||||
for i in start..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt: continue
|
||||
of nkIdentDefs, nkVarTuple, nkConstDef:
|
||||
checkMinSonsLen(a, 3, c.c.config)
|
||||
when defined(nimsuggest):
|
||||
inc c.c.inTypeContext
|
||||
a[^2] = semTemplBody(c, a[^2])
|
||||
when defined(nimsuggest):
|
||||
dec c.c.inTypeContext
|
||||
a[^1] = semTemplBody(c, a[^1])
|
||||
for j in 0..<a.len-2:
|
||||
addLocalDecl(c, a[j], symKind)
|
||||
semTemplIdentDef(c, a, symKind)
|
||||
else:
|
||||
illFormedAst(a, c.c.config)
|
||||
|
||||
@@ -481,6 +464,25 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
closeScope(c)
|
||||
else:
|
||||
a[2] = semTemplBody(c, a[2])
|
||||
of nkObjectTy:
|
||||
openScope(c)
|
||||
result = semTemplBodySons(c, n)
|
||||
closeScope(c)
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
case a.kind:
|
||||
of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
|
||||
of nkIdentDefs:
|
||||
semTemplIdentDef(c, a, skField)
|
||||
of nkRecCase, nkRecWhen:
|
||||
n[i] = semTemplBody(c, a)
|
||||
else:
|
||||
illFormedAst(a, c.c.config)
|
||||
of nkRecCase:
|
||||
semTemplIdentDef(c, n[0], skField)
|
||||
for i in 1..<n.len:
|
||||
n[i] = semTemplBody(c, n[i])
|
||||
of nkProcDef, nkLambdaKinds:
|
||||
result = semRoutineInTemplBody(c, n, skProc)
|
||||
of nkFuncDef:
|
||||
@@ -504,14 +506,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
if x.kind == nkExprColonExpr:
|
||||
x[1] = semTemplBody(c, x[1])
|
||||
of nkBracketExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
if n.typ == nil:
|
||||
# if a[b] is nested inside a typed expression, don't convert it
|
||||
# back to `[]`(a, b), prepareOperand will not typecheck it again
|
||||
# and so `[]` will not be resolved
|
||||
# checking if a[b] is typed should be enough to cover this case
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
if n.typ == nil:
|
||||
# see nkBracketExpr case for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
|
||||
for i in 0..<n.len: result.add(n[i])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
checkSonsLen(n, 2, c.c.config)
|
||||
@@ -521,17 +530,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
let k = a.kind
|
||||
case k
|
||||
of nkBracketExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
if a.typ == nil:
|
||||
# see nkBracketExpr case above for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
let a0 = semTemplBody(c, a[0])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
if a.typ == nil:
|
||||
# see nkBracketExpr case above for explanation
|
||||
result = newNodeI(nkCall, n.info)
|
||||
result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
result = semTemplBodySons(c, result)
|
||||
else:
|
||||
result = semTemplBodySons(c, n)
|
||||
@@ -544,6 +557,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
# so we use the generic code for nkDotExpr too
|
||||
let s = qualifiedLookUp(c.c, n, {})
|
||||
if s != nil:
|
||||
# mirror the nkIdent case
|
||||
# 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:
|
||||
@@ -555,13 +569,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
elif contains(c.toMixin, s.name.id):
|
||||
return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
|
||||
else:
|
||||
return symChoice(c.c, n, s, scOpen, c.noGenSym > 0)
|
||||
if s.kind in {skType, skVar, skLet, skConst}:
|
||||
discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
|
||||
return semTemplSymbol(c.c, n, s, c.noGenSym > 0)
|
||||
if n.kind == nkDotExpr:
|
||||
result = n
|
||||
result[0] = semTemplBody(c, n[0])
|
||||
inc c.noGenSym
|
||||
result[1] = semTemplBody(c, n[1])
|
||||
dec c.noGenSym
|
||||
if result[1].kind == nkSym and result[1].sym.kind in routineKinds:
|
||||
# prevent `dotTransformation` from rewriting this node to `nkIdent`
|
||||
# by making it a symchoice
|
||||
# in generics this becomes `nkClosedSymChoice` but this breaks code
|
||||
# as the old behavior here was that this became `nkIdent`
|
||||
var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c))
|
||||
choice.add result[1]
|
||||
result[1] = choice
|
||||
else:
|
||||
result = semTemplBodySons(c, n)
|
||||
of nkExprColonExpr, nkExprEqExpr:
|
||||
@@ -626,6 +650,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
onDef(n[namePos].info, s)
|
||||
# check parameter list:
|
||||
#s.scope = c.currentScope
|
||||
# push noalias flag at first to prevent unwanted recursive calls:
|
||||
incl(s.flags, sfNoalias)
|
||||
pushOwner(c, s)
|
||||
openScope(c)
|
||||
n[namePos] = newSymNode(s)
|
||||
@@ -635,17 +661,23 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
setGenericParamsMisc(c, n)
|
||||
# process parameters:
|
||||
var allUntyped = true
|
||||
var nullary = true
|
||||
if n[paramsPos].kind != nkEmpty:
|
||||
semParamList(c, n[paramsPos], n[genericParamsPos], s)
|
||||
# a template's parameters are not gensym'ed even if that was originally the
|
||||
# case as we determine whether it's a template parameter in the template
|
||||
# body by the absence of the sfGenSym flag:
|
||||
let retType = s.typ[0]
|
||||
if retType != nil and retType.kind != tyUntyped:
|
||||
allUntyped = false
|
||||
for i in 1..<s.typ.n.len:
|
||||
let param = s.typ.n[i].sym
|
||||
if param.name.s != "_":
|
||||
if param.name.id != ord(wUnderscore):
|
||||
param.flags.incl sfTemplateParam
|
||||
param.flags.excl sfGenSym
|
||||
if param.typ.kind != tyUntyped: allUntyped = false
|
||||
# no default value, parameters required in call
|
||||
if param.ast == nil: nullary = false
|
||||
else:
|
||||
s.typ = newTypeS(tyProc, c)
|
||||
# XXX why do we need tyTyped as a return type again?
|
||||
@@ -657,6 +689,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
|
||||
n[genericParamsPos] = n[miscPos][1]
|
||||
n[miscPos] = c.graph.emptyNode
|
||||
if allUntyped: incl(s.flags, 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)
|
||||
|
||||
if n[patternPos].kind != nkEmpty:
|
||||
n[patternPos] = semPattern(c, n[patternPos], s)
|
||||
|
||||
@@ -16,6 +16,7 @@ const
|
||||
errIntLiteralExpected = "integer literal expected"
|
||||
errWrongNumberOfVariables = "wrong number of variables"
|
||||
errInvalidOrderInEnumX = "invalid order in enum '$1'"
|
||||
errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
|
||||
errOrdinalTypeExpected = "ordinal type expected; given: $1"
|
||||
errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
|
||||
errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal"
|
||||
@@ -130,8 +131,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
e.typ = result
|
||||
e.position = int(counter)
|
||||
let symNode = newSymNode(e)
|
||||
if optNimV1Emulation notin c.config.globalOptions and identToReplace != nil and
|
||||
c.config.cmd notin cmdDocLike: # A hack to produce documentation for enum fields.
|
||||
if identToReplace != nil and c.config.cmd notin cmdDocLike:
|
||||
# A hack to produce documentation for enum fields.
|
||||
identToReplace[] = symNode
|
||||
if e.position == 0: hasNull = true
|
||||
if result.sym != nil and sfExported in result.sym.flags:
|
||||
@@ -140,6 +141,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
result.n.add symNode
|
||||
styleCheckDef(c, e)
|
||||
onDef(e.info, e)
|
||||
suggestSym(c.graph, e.info, e, c.graph.usageSym)
|
||||
if sfGenSym notin e.flags:
|
||||
if not isPure:
|
||||
addInterfaceOverloadableSymAt(c, c.currentScope, e)
|
||||
@@ -147,7 +149,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
declarePureEnumField(c, e)
|
||||
if (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
|
||||
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
||||
inc(counter)
|
||||
if counter == high(typeof(counter)):
|
||||
if i > 1 and result.n[i-2].sym.position == high(int):
|
||||
localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
|
||||
else:
|
||||
inc(counter)
|
||||
if isPure and sfExported in result.sym.flags:
|
||||
addPureEnum(c, LazySym(sym: result.sym))
|
||||
if tfNotNil in e.typ.flags and not hasNull:
|
||||
@@ -161,7 +167,9 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||
addSonSkipIntLit(result, base, c.idgen)
|
||||
if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
|
||||
if base.kind notin {tyGenericParam, tyGenericInvocation}:
|
||||
if not isOrdinalType(base, allowEnumWithHoles = true):
|
||||
if base.kind == tyForward:
|
||||
c.skipTypes.add n
|
||||
elif not isOrdinalType(base, allowEnumWithHoles = true):
|
||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
|
||||
elif lengthOrd(c.config, base) > MaxSetElements:
|
||||
localError(c.config, n.info, errSetTooBig)
|
||||
@@ -221,16 +229,27 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
|
||||
return false
|
||||
|
||||
proc fitDefaultNode(c: PContext, n: PNode): PType =
|
||||
inc c.inStaticContext
|
||||
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||
let oldType = n[^1].typ
|
||||
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
|
||||
let oldType = n[^1].typ
|
||||
n[^1].flags.incl nfSem
|
||||
if n[^2].kind != nkEmpty:
|
||||
if expectedType != nil and oldType != expectedType:
|
||||
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
|
||||
changeType(c, n[^1], expectedType, true) # infer types for default fields value
|
||||
# bug #22926; be cautious that it uses `semConstExpr` to
|
||||
# evaulate the default fields; it's only natural to use
|
||||
# `changeType` to infer types for constant values
|
||||
# that's also the reason why we don't use `semExpr` to check
|
||||
# the type since two overlapping error messages might be produced
|
||||
result = n[^1].typ
|
||||
else:
|
||||
result = n[^1].typ
|
||||
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
|
||||
if n[^1].kind != nkNilLit:
|
||||
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||
dec c.inStaticContext
|
||||
|
||||
proc isRecursiveType*(t: PType): bool =
|
||||
# handle simple recusive types before typeFinalPass
|
||||
@@ -264,6 +283,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n.info, "range is empty")
|
||||
|
||||
var range: array[2, PNode]
|
||||
# XXX this is still a hard compilation in a generic context, this can
|
||||
# result in unresolved generic parameters being treated like real types
|
||||
range[0] = semExprWithType(c, n[1], {efDetermineType})
|
||||
range[1] = semExprWithType(c, n[2], {efDetermineType})
|
||||
|
||||
@@ -272,7 +293,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
|
||||
|
||||
let hasUnknownTypes = c.inGenericContext > 0 and
|
||||
rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr
|
||||
(rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr)
|
||||
|
||||
if not hasUnknownTypes:
|
||||
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
|
||||
@@ -280,7 +301,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
|
||||
elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or
|
||||
rangeT[0].kind == tyBool:
|
||||
localError(c.config, n.info, "ordinal or float type expected")
|
||||
localError(c.config, n.info, "ordinal or float type expected, but got " & typeToString(rangeT[0]))
|
||||
elif enumHasHoles(rangeT[0]):
|
||||
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||
|
||||
@@ -291,9 +312,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
result.n.add semConstExpr(c, range[i])
|
||||
|
||||
if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and result.n[0].floatVal.isNaN) or
|
||||
(result.n[1].kind in {nkFloatLit..nkFloat64Lit} and result.n[1].floatVal.isNaN):
|
||||
localError(c.config, n.info, "NaN is not a valid start or end for a range")
|
||||
if result.n[i].kind in {nkFloatLit..nkFloat64Lit} and result.n[i].floatVal.isNaN:
|
||||
localError(c.config, n.info, "NaN is not a valid range " & (if i == 0: "start" else: "end"))
|
||||
|
||||
if weakLeValue(result.n[0], result.n[1]) == impNo:
|
||||
localError(c.config, n.info, "range is empty")
|
||||
@@ -327,12 +347,22 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n.info, errXExpectsOneTypeParam % "range")
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
|
||||
proc semArrayIndexConst(c: PContext, e: PNode, info: TLineInfo): PType =
|
||||
let x = semConstExpr(c, e)
|
||||
if x.kind in {nkIntLit..nkUInt64Lit}:
|
||||
result = makeRangeType(c, 0, x.intVal-1, info,
|
||||
x.typ.skipTypes({tyTypeDesc}))
|
||||
else:
|
||||
result = x.typ.skipTypes({tyTypeDesc})
|
||||
|
||||
proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
if isRange(n):
|
||||
result = semRangeAux(c, n, nil)
|
||||
elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<":
|
||||
result = errorType(c)
|
||||
else:
|
||||
# XXX this is still a hard compilation in a generic context, this can
|
||||
# result in unresolved generic parameters being treated like real types
|
||||
let e = semExprWithType(c, n, {efDetermineType})
|
||||
if e.typ.kind == tyFromExpr:
|
||||
result = makeRangeWithStaticExpr(c, e.typ.n)
|
||||
@@ -341,19 +371,23 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
localError(c.config, n.info,
|
||||
"Array length can't be negative, but was " & $e.intVal)
|
||||
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
||||
if e.sym.ast != nil:
|
||||
return semArrayIndex(c, e.sym.ast)
|
||||
if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon):
|
||||
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
|
||||
elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc) :
|
||||
if e.typ.kind == tyStatic:
|
||||
if e.sym.ast != nil:
|
||||
return semArrayIndex(c, e.sym.ast)
|
||||
if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon):
|
||||
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
|
||||
else:
|
||||
result = e.typ.skipTypes({tyTypeDesc})
|
||||
result.flags.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))
|
||||
# This is an int returning call, depending on an
|
||||
# yet unknown generic param (see tgenericshardcases).
|
||||
# yet unknown generic param (see tuninstantiatedgenericcalls).
|
||||
# We are going to construct a range type that will be
|
||||
# properly filled-out in semtypinst (see how tyStaticExpr
|
||||
# is handled there).
|
||||
@@ -361,12 +395,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
elif e.kind == nkIdent:
|
||||
result = e.typ.skipTypes({tyTypeDesc})
|
||||
else:
|
||||
let x = semConstExpr(c, e)
|
||||
if x.kind in {nkIntLit..nkUInt64Lit}:
|
||||
result = makeRangeType(c, 0, x.intVal-1, n.info,
|
||||
x.typ.skipTypes({tyTypeDesc}))
|
||||
else:
|
||||
result = x.typ.skipTypes({tyTypeDesc})
|
||||
result = semArrayIndexConst(c, e, n.info)
|
||||
#localError(c.config, n[1].info, errConstExprExpected)
|
||||
|
||||
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
@@ -376,14 +405,18 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
let indx = semArrayIndex(c, n[1])
|
||||
var indxB = indx
|
||||
if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB)
|
||||
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
|
||||
if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}:
|
||||
discard
|
||||
elif not isOrdinalType(indxB):
|
||||
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and
|
||||
tfUnresolved notin indxB.flags:
|
||||
if not isOrdinalType(indxB):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc))
|
||||
elif enumHasHoles(indxB):
|
||||
localError(c.config, n[1].info, "enum '$1' has holes" %
|
||||
typeToString(indxB.skipTypes({tyRange})))
|
||||
elif indxB.kind != tyRange and
|
||||
lengthOrd(c.config, indxB) > high(uint16).int:
|
||||
# assume range type is intentional
|
||||
localError(c.config, n[1].info,
|
||||
"index type '$1' for array is too large" % typeToString(indxB))
|
||||
base = semTypeNode(c, n[2], nil)
|
||||
# ensure we only construct a tyArray when there was no error (bug #3048):
|
||||
result = newOrPrevType(tyArray, prev, c)
|
||||
@@ -416,68 +449,6 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal")
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkSym:
|
||||
result = getGenSym(c, n.sym)
|
||||
else:
|
||||
result = pickSym(c, n, {skType, skGenericParam, skParam})
|
||||
if result.isNil:
|
||||
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
|
||||
if result != nil:
|
||||
markUsed(c, n.info, result)
|
||||
onUse(n.info, result)
|
||||
|
||||
if result.kind == skParam and result.typ.kind == tyTypeDesc:
|
||||
# This is a typedesc param. is it already bound?
|
||||
# it's not bound when it's used multiple times in the
|
||||
# proc signature for example
|
||||
if c.inGenericInst > 0:
|
||||
let bound = result.typ[0].sym
|
||||
if bound != nil: return bound
|
||||
return result
|
||||
if result.typ.sym == nil:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
result = result.typ.sym.copySym(nextSymId c.idgen)
|
||||
result.typ = exactReplica(result.typ)
|
||||
result.typ.flags.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
|
||||
return
|
||||
else:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
|
||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||
var ov: TOverloadIter
|
||||
var amb = initOverloadIter(ov, c, n)
|
||||
while amb != nil and amb.kind != skType:
|
||||
amb = nextOverloadIter(ov, c, n)
|
||||
if amb != nil: result = amb
|
||||
else:
|
||||
if result.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
if result.typ.kind != tyGenericParam:
|
||||
# XXX get rid of this hack!
|
||||
var oldInfo = n.info
|
||||
when defined(useNodeIds):
|
||||
let oldId = n.id
|
||||
reset(n[])
|
||||
when defined(useNodeIds):
|
||||
n.id = oldId
|
||||
n.transitionNoneToSym()
|
||||
n.sym = result
|
||||
n.info = oldInfo
|
||||
n.typ = result.typ
|
||||
else:
|
||||
localError(c.config, n.info, "identifier expected")
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n.len == 0:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
@@ -565,6 +536,12 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||
else: discard
|
||||
else:
|
||||
result = semIdentVis(c, kind, n, allowed)
|
||||
case kind
|
||||
of skField: implicitPragmas(c, result, n.info, fieldPragmas)
|
||||
of skVar: implicitPragmas(c, result, n.info, varPragmas)
|
||||
of skLet: implicitPragmas(c, result, n.info, letPragmas)
|
||||
of skConst: implicitPragmas(c, result, n.info, constPragmas)
|
||||
else: discard
|
||||
|
||||
proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
|
||||
let ex = t[branchIndex][currentEx].skipConv
|
||||
@@ -795,7 +772,13 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
if e.kind != nkIntLit: discard "don't report followup error"
|
||||
elif e.intVal != 0 and branch == nil: branch = it[1]
|
||||
else:
|
||||
it[0] = forceBool(c, semExprWithType(c, it[0]))
|
||||
# XXX this is still a hard compilation in a generic context, this can
|
||||
# result in unresolved generic parameters being treated like real types
|
||||
let e = semExprWithType(c, it[0], {efDetermineType})
|
||||
if e.typ.kind == tyFromExpr:
|
||||
it[0] = makeStaticExpr(c, e)
|
||||
else:
|
||||
it[0] = forceBool(c, e)
|
||||
of nkElse:
|
||||
checkSonsLen(it, 1, c.config)
|
||||
if branch == nil: branch = it[0]
|
||||
@@ -803,8 +786,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
else: illFormedAst(n, c.config)
|
||||
if c.inGenericContext > 0:
|
||||
# use a new check intset here for each branch:
|
||||
var newCheck: IntSet
|
||||
assign(newCheck, check)
|
||||
var newCheck: IntSet = check
|
||||
var newPos = pos
|
||||
var newf = newNodeI(nkRecList, n.info)
|
||||
semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
|
||||
@@ -912,12 +894,19 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
|
||||
while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink, tyOwned}:
|
||||
result = lastSon(result)
|
||||
|
||||
proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
|
||||
obj: PType) =
|
||||
assert obj.kind == tyObject
|
||||
if (obj.len > 0) and (obj[0] != nil):
|
||||
addInheritedFields(c, check, pos, obj[0].skipGenericInvocation)
|
||||
addInheritedFieldsAux(c, check, pos, obj.n)
|
||||
proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
|
||||
obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
|
||||
if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
|
||||
(innerObj != nil and obj.sym.id == innerObj.sym.id):
|
||||
localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
|
||||
result = false
|
||||
elif obj.kind == tyObject:
|
||||
result = true
|
||||
if (obj.len > 0) and (obj[0] != nil):
|
||||
result = result and tryAddInheritedFields(c, check, pos, obj[0].skipGenericInvocation, n, false, obj)
|
||||
addInheritedFieldsAux(c, check, pos, obj.n)
|
||||
else:
|
||||
result = true
|
||||
|
||||
proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
|
||||
if n.len == 0:
|
||||
@@ -944,7 +933,11 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
if concreteBase.sym != nil and concreteBase.sym.magic == mException and
|
||||
sfSystemModule notin c.module.flags:
|
||||
message(c.config, n.info, warnInheritFromException, "")
|
||||
addInheritedFields(c, check, pos, concreteBase)
|
||||
if not tryAddInheritedFields(c, check, pos, concreteBase, n):
|
||||
return newType(tyError, nextTypeId c.idgen, result.owner)
|
||||
|
||||
elif concreteBase.kind == tyForward:
|
||||
c.skipTypes.add n #we retry in the final pass
|
||||
else:
|
||||
if concreteBase.kind != tyError:
|
||||
localError(c.config, n[1].info, "inheritance only works with non-final objects; " &
|
||||
@@ -962,7 +955,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
result.n = newNodeI(nkRecList, n.info)
|
||||
else:
|
||||
# partial object so add things to the check
|
||||
addInheritedFields(c, check, pos, result)
|
||||
if not tryAddInheritedFields(c, check, pos, result, n, isPartial = true):
|
||||
return newType(tyError, nextTypeId c.idgen, result.owner)
|
||||
|
||||
semRecordNodeAux(c, n[2], check, pos, result.n, result)
|
||||
if n[0].kind != nkEmpty:
|
||||
# dummy symbol for `pragma`:
|
||||
@@ -1026,7 +1021,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
t.rawAddSonNoPropagationOfTypeFlags result
|
||||
result = t
|
||||
else: discard
|
||||
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc}:
|
||||
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
result.flags.incl tfHasAsgn
|
||||
|
||||
proc findEnforcedStaticType(t: PType): PType =
|
||||
@@ -1044,7 +1039,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||
if kind == skMacro:
|
||||
let staticType = findEnforcedStaticType(param.typ)
|
||||
if staticType != nil:
|
||||
var a = copySym(param, nextSymId c.idgen)
|
||||
var a = copySym(param, c.idgen)
|
||||
a.typ = staticType.base
|
||||
addDecl(c, a)
|
||||
#elif param.typ != nil and param.typ.kind == tyTypeDesc:
|
||||
@@ -1052,7 +1047,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||
else:
|
||||
# within a macro, every param has the type NimNode!
|
||||
let nn = getSysSym(c.graph, param.info, "NimNode")
|
||||
var a = copySym(param, nextSymId c.idgen)
|
||||
var a = copySym(param, c.idgen)
|
||||
a.typ = nn.typ
|
||||
addDecl(c, a)
|
||||
else:
|
||||
@@ -1082,7 +1077,7 @@ proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
|
||||
|
||||
let owner = if typeClass.sym != nil: typeClass.sym
|
||||
else: getCurrOwner(c)
|
||||
var s = newSym(skType, finalTypId, nextSymId c.idgen, owner, info)
|
||||
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)
|
||||
s.linkTo(typeClass)
|
||||
@@ -1311,6 +1306,16 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
|
||||
if hasDefault:
|
||||
def = a[^1]
|
||||
if a.len > 3:
|
||||
var msg = ""
|
||||
for j in 0 ..< a.len - 2:
|
||||
if msg.len != 0: msg.add(", ")
|
||||
msg.add($a[j])
|
||||
msg.add(" all have default value '")
|
||||
msg.add(def.renderTree)
|
||||
msg.add("', this may be unintentional, " &
|
||||
"either use ';' (semicolon) or explicitly write each default value")
|
||||
message(c.config, a.info, warnImplicitDefaultValue, msg)
|
||||
block determineType:
|
||||
var defTyp = typ
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
@@ -1359,6 +1364,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
|
||||
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)
|
||||
elif containsOrIncl(check, arg.name.id):
|
||||
localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||
if a[j].kind == nkPragmaExpr:
|
||||
pragma(c, arg, a[j][1], paramPragmas)
|
||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
||||
@@ -1367,26 +1376,26 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
else:
|
||||
localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s)
|
||||
typ = errorType(c)
|
||||
var nameForLift = arg.name.s
|
||||
if sfGenSym in arg.flags:
|
||||
nameForLift.add("`gensym" & $arg.id)
|
||||
let lifted = liftParamType(c, kind, genericParams, typ,
|
||||
arg.name.s, arg.info)
|
||||
nameForLift, arg.info)
|
||||
let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen)
|
||||
arg.typ = finalType
|
||||
arg.position = counter
|
||||
arg.constraint = constraint
|
||||
if constraint != nil:
|
||||
#only replace the constraint when it has been set as arg could contain codegenDecl
|
||||
arg.constraint = constraint
|
||||
inc(counter)
|
||||
if def != nil and def.kind != nkEmpty:
|
||||
arg.ast = copyTree(def)
|
||||
if arg.name.s == "_":
|
||||
arg.flags.incl(sfGenSym)
|
||||
elif containsOrIncl(check, arg.name.id):
|
||||
localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||
result.n.add newSymNode(arg)
|
||||
rawAddSon(result, finalType)
|
||||
addParamOrResult(c, arg, kind)
|
||||
styleCheckDef(c, a[j].info, arg)
|
||||
onDef(a[j].info, arg)
|
||||
if {optNimV1Emulation, optNimV12Emulation} * c.config.globalOptions == {}:
|
||||
a[j] = newSymNode(arg)
|
||||
a[j] = newSymNode(arg)
|
||||
|
||||
var r: PType
|
||||
if n[0].kind != nkEmpty:
|
||||
@@ -1413,7 +1422,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
"' is only valid for macros and templates")
|
||||
# 'auto' as a return type does not imply a generic:
|
||||
elif r.kind == tyAnything:
|
||||
discard
|
||||
r = copyType(r, nextTypeId c.idgen, r.owner)
|
||||
r.flags.incl tfRetType
|
||||
elif r.kind == tyStatic:
|
||||
# type allowed should forbid this type
|
||||
discard
|
||||
@@ -1481,25 +1491,35 @@ proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
|
||||
result = semTypeNode(c, n, nil)
|
||||
n.typ = makeTypeDesc(c, result)
|
||||
|
||||
proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
|
||||
proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
|
||||
var
|
||||
check = initIntSet()
|
||||
pos = 0
|
||||
let
|
||||
realBase = t[0]
|
||||
base = skipTypesOrNil(realBase, skipPtrs)
|
||||
result = true
|
||||
if base.isNil:
|
||||
localError(c.config, n.info, errIllegalRecursionInTypeX % "object")
|
||||
else:
|
||||
let concreteBase = skipGenericInvocation(base)
|
||||
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
|
||||
addInheritedFields(c, check, pos, concreteBase)
|
||||
if not tryAddInheritedFields(c, check, pos, concreteBase, n):
|
||||
return false
|
||||
else:
|
||||
if concreteBase.kind != tyError:
|
||||
localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects)
|
||||
var newf = newNodeI(nkRecList, n.info)
|
||||
semRecordNodeAux(c, t.n, check, pos, newf, t)
|
||||
|
||||
proc containsGenericInvocationWithForward(n: PNode): bool =
|
||||
if n.kind == nkSym and n.sym.ast != nil and n.sym.ast.len > 1 and n.sym.ast[2].kind == nkObjectTy:
|
||||
for p in n.sym.ast[2][^1]:
|
||||
if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and
|
||||
p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if s.typ == nil:
|
||||
localError(c.config, n.info, "cannot instantiate the '$1' $2" %
|
||||
@@ -1509,20 +1529,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
var t = s.typ.skipTypes({tyAlias})
|
||||
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||
t = t.base
|
||||
|
||||
result = newOrPrevType(tyGenericInvocation, prev, c)
|
||||
addSonSkipIntLit(result, t, c.idgen)
|
||||
|
||||
template addToResult(typ) =
|
||||
template addToResult(typ, skip) =
|
||||
|
||||
if typ.isNil:
|
||||
internalAssert c.config, false
|
||||
rawAddSon(result, typ)
|
||||
else: addSonSkipIntLit(result, typ, c.idgen)
|
||||
else:
|
||||
if skip:
|
||||
addSonSkipIntLit(result, typ, c.idgen)
|
||||
else:
|
||||
rawAddSon(result, makeRangeWithStaticExpr(c, typ.n))
|
||||
|
||||
if t.kind == tyForward:
|
||||
for i in 1..<n.len:
|
||||
var elem = semGenericParamInInvocation(c, n[i])
|
||||
addToResult(elem)
|
||||
addToResult(elem, true)
|
||||
return
|
||||
elif t.kind != tyGenericBody:
|
||||
# we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
@@ -1542,7 +1566,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
|
||||
var isConcrete = true
|
||||
|
||||
let rType = m.call[0].typ
|
||||
let mIndex = if rType != nil: rType.len - 1 else: -1
|
||||
for i in 1..<m.call.len:
|
||||
var typ = m.call[i].typ
|
||||
# is this a 'typedesc' *parameter*? If so, use the typedesc type,
|
||||
@@ -1550,17 +1575,22 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if m.call[i].kind == nkSym and m.call[i].sym.kind == skParam and
|
||||
typ.kind == tyTypeDesc and containsGenericType(typ):
|
||||
isConcrete = false
|
||||
addToResult(typ)
|
||||
addToResult(typ, true)
|
||||
else:
|
||||
typ = typ.skipTypes({tyTypeDesc})
|
||||
if containsGenericType(typ): isConcrete = false
|
||||
addToResult(typ)
|
||||
var skip = true
|
||||
if mIndex >= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ):
|
||||
skip = false
|
||||
addToResult(typ, skip)
|
||||
|
||||
if isConcrete:
|
||||
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||
# XXX: What kind of error is this? is it still relevant?
|
||||
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif containsGenericInvocationWithForward(n[0]):
|
||||
c.skipTypes.add n #fixes 1500
|
||||
else:
|
||||
result = instGenericContainer(c, n.info, result,
|
||||
allowMetaTypes = false)
|
||||
@@ -1573,7 +1603,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
return errorType(c)
|
||||
if tx != result and tx.kind == tyObject:
|
||||
if tx[0] != nil:
|
||||
semObjectTypeForInheritedGenericInst(c, n, tx)
|
||||
if not trySemObjectTypeForInheritedGenericInst(c, n, tx):
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
var position = 0
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
|
||||
@@ -1684,7 +1715,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
|
||||
internalAssert c.config, dummyName.kind == nkIdent
|
||||
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
|
||||
dummyName.ident, nextSymId c.idgen, owner, param.info)
|
||||
dummyName.ident, c.idgen, owner, param.info)
|
||||
dummyParam.typ = dummyType
|
||||
incl dummyParam.flags, sfUsed
|
||||
addDecl(c, dummyParam)
|
||||
@@ -1748,7 +1779,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||
if n[1].kind != nkEmpty and n[1].len > 0:
|
||||
pragma(c, s, n[1], procTypePragmas)
|
||||
when useEffectSystem: setEffectsForProcType(c.graph, result, n[1])
|
||||
elif c.optionStack.len > 0 and optNimV1Emulation notin c.config.globalOptions:
|
||||
elif c.optionStack.len > 0:
|
||||
# we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'...
|
||||
s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info),
|
||||
newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info))
|
||||
@@ -1790,6 +1821,73 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
fixupTypeOf(c, prev, t)
|
||||
result = t.typ
|
||||
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkSym:
|
||||
result = getGenSym(c, n.sym)
|
||||
else:
|
||||
result = pickSym(c, n, {skType, skGenericParam, skParam})
|
||||
if result.isNil:
|
||||
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
|
||||
if result != nil:
|
||||
markUsed(c, n.info, result)
|
||||
onUse(n.info, result)
|
||||
|
||||
# alias syntax, see semSym for skTemplate, skMacro
|
||||
if result.kind in {skTemplate, skMacro} and sfNoalias notin result.flags:
|
||||
let t = semTypeExpr(c, n, nil)
|
||||
result = symFromType(c, t, n.info)
|
||||
|
||||
if result.kind == skParam and result.typ.kind == tyTypeDesc:
|
||||
# This is a typedesc param. is it already bound?
|
||||
# it's not bound when it's used multiple times in the
|
||||
# proc signature for example
|
||||
if c.inGenericInst > 0:
|
||||
let bound = result.typ[0].sym
|
||||
if bound != nil: return bound
|
||||
return result
|
||||
if result.typ.sym == nil:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
result = result.typ.sym.copySym(c.idgen)
|
||||
result.typ = exactReplica(result.typ)
|
||||
result.typ.flags.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
|
||||
return
|
||||
else:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
|
||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||
var ov: TOverloadIter
|
||||
var amb = initOverloadIter(ov, c, n)
|
||||
while amb != nil and amb.kind != skType:
|
||||
amb = nextOverloadIter(ov, c, n)
|
||||
if amb != nil: result = amb
|
||||
else:
|
||||
if result.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
return errorSym(c, n)
|
||||
if result.typ.kind != tyGenericParam:
|
||||
# XXX get rid of this hack!
|
||||
var oldInfo = n.info
|
||||
when defined(useNodeIds):
|
||||
let oldId = n.id
|
||||
reset(n[])
|
||||
when defined(useNodeIds):
|
||||
n.id = oldId
|
||||
n.transitionNoneToSym()
|
||||
n.sym = result
|
||||
n.info = oldInfo
|
||||
n.typ = result.typ
|
||||
else:
|
||||
localError(c.config, n.info, "identifier expected")
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = nil
|
||||
inc c.inTypeContext
|
||||
@@ -1832,7 +1930,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = semTypeExpr(c, n, prev)
|
||||
else:
|
||||
let op = considerQuotedIdent(c, n[0])
|
||||
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
|
||||
if op.id == ord(wAnd) or op.id == ord(wOr) or op.s == "|":
|
||||
checkSonsLen(n, 3, c.config)
|
||||
var
|
||||
t1 = semTypeNode(c, n[1], nil)
|
||||
@@ -1909,6 +2007,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
else:
|
||||
if c.inGenericContext > 0 and n.kind == nkCall:
|
||||
let n = semGenericStmt(c, n)
|
||||
result = makeTypeFromExpr(c, n.copyTree)
|
||||
else:
|
||||
result = semTypeExpr(c, n, prev)
|
||||
@@ -2043,25 +2142,32 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of nkOutTy: result = semVarOutType(c, n, prev, {tfIsOutParam})
|
||||
of nkDistinctTy: result = semDistinct(c, n, prev)
|
||||
of nkStaticTy: result = semStaticType(c, n[0], prev)
|
||||
of nkIteratorTy:
|
||||
if n.len == 0:
|
||||
of nkProcTy, nkIteratorTy:
|
||||
if n.len == 0 or n[0].kind == nkEmpty:
|
||||
# 0 length or empty param list with possible pragmas imply typeclass
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
let child = newTypeS(tyProc, c)
|
||||
child.flags.incl tfIterator
|
||||
if n.kind == nkIteratorTy:
|
||||
child.flags.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
|
||||
# dummy symbol for `pragma`:
|
||||
var s = newSymS(symKind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
|
||||
s.typ = child
|
||||
# for now only call convention pragmas supported in proc typeclass
|
||||
pragma(c, s, n[1], {FirstCallConv..LastCallConv})
|
||||
result.addSonSkipIntLit(child, c.idgen)
|
||||
else:
|
||||
result = semProcTypeWithScope(c, n, prev, skIterator)
|
||||
if result.kind == tyProc:
|
||||
let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
|
||||
result = semProcTypeWithScope(c, n, prev, symKind)
|
||||
if result == nil:
|
||||
localError(c.config, n.info, "type expected, but got: " & renderTree(n))
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
if n.kind == nkIteratorTy and result.kind == tyProc:
|
||||
result.flags.incl(tfIterator)
|
||||
if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
|
||||
result.callConv = ccInline
|
||||
else:
|
||||
result.callConv = ccClosure
|
||||
of nkProcTy:
|
||||
if n.len == 0:
|
||||
result = newConstraint(c, tyProc)
|
||||
else:
|
||||
result = semProcTypeWithScope(c, n, prev, skProc)
|
||||
if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
result.flags.incl tfHasAsgn
|
||||
of nkEnumTy: result = semEnum(c, n, prev)
|
||||
of nkType: result = n.typ
|
||||
of nkStmtListType: result = semStmtListType(c, n, prev)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user